Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --file formatting:
- --name size(1), name([1]), address(16), size of size (1), size([3]), file([4])
- --this file is designed to be require()ed
- --implements raid4 array on unmanaged drives
- --this version is for use with the server drive group stuff, rather than accessing its own drives
- local comp = require("component")
- local network = comp.modem
- local event = require("event")
- local raidmanager = {} --for holding the methods and such
- _G.driveServerTbl = _G.driveServerTbl or {}
- if _G.driveServerCount == nil then
- error("No global driveServerCount found")
- goto exit
- elseif type(_G.driveServerCount) ~= "number" then
- error("Global driveServerCount found but it is not a number")
- goto exit
- elseif _G.driveServerCount ~= math.floor(_G.driveServerCount) or _G.driveServerCount < 1 then
- error("Global driveServerCount found but it is an invalid number (" .. _G.driveServerCount .. ")")
- goto exit
- end
- serverCount = _G.driveServerCount
- network.open(12345)
- if #(_G.driveServerTbl) == 0 then --set _G.driveServerTbl to {} or nil to reset. Or 0, I guess?
- network.broadcast(12345, "raidConnectionCheck")
- for i=1,serverCount do
- local _, _, server, _, _, confirm = event.pull("modem_message")
- _G.driveServerTbl[i] = server
- print(server)
- end
- end
- raidmanager.getFileSize = function(driveDex, startLoc)
- local drive =_G.driveServerTbl[driveDex]
- local currLoc = startLoc
- network.send(drive, 12345, "readByte", currLoc)
- local _, _, _, _, _, nameSize = event.pull("modem_message")
- currLoc = currLoc + 1 + nameSize --nameSize and name
- currLoc = currLoc + 16 --address
- network.send(drive, 12345, "readByte", currLoc)
- local _, _, _, _, _, sizeSize = event.pull("modem_message")
- --would add 1 but do that later
- local size = 1
- for i=1, sizeSize do
- network.send(drive, 12345, "readByte", currLoc + i)
- local _, _, _, _, _, thisbyte = event.pull("modem_message")
- size = 256*size + string.byte(thisbyte)
- end
- return 1 + nameSize + 16 + 1 + sizeSize + size --namesize, name, address, sizesize, size, data
- end
- --simplifies a little so we don't need 3 of the same code in each of these
- local sendReceive = function(address, arg1, arg2, arg3, arg4)
- network.send(address, 12345, arg1, arg2, arg3, arg4)
- local results = table.pack(event.pull("modem_message"))
- print(table.unpack(results))
- if #results == 6 then
- return results[6]
- end
- local subResults = {}
- for i=6, #results do
- subResults[i-5] = results[i]
- end
- return subResults
- end
- --this gets the size the given data's file would be rather than the size of an existing file on the drive
- raidmanager.getDataSize = function(name, data)
- local nameSize = string.len(name)
- local size = string.len(data)
- local sizeSize = math.ceil(math.log(size+1)/math.log(256))
- return 1 + nameSize + 16 + 1 + sizeSize + size
- end
- raidmanager.nextEmptySpace = function(driveDex)
- local drive =_G.driveServerTbl[driveDex]
- local currLoc = 1
- --no file can have an empty name so a 0 in nameSize indicates no file
- while currLoc < sendReceive(drive, "getCapacity") and sendReceive(drive, "readByte", currLoc) do
- currLoc = currLoc + raidmanager.getFileSize(driveDex, currLoc)
- end
- return currLoc
- end
- raidmanager.getMinCapacity = function()
- local minSize = math.huge
- for i=2,#_G.driveServerTbl do --skip 1 since that's parity
- minSize = math.min(minSize, tonumber(sendReceive(_G.driveServerTbl[i], "getCapacity")))
- end
- return minSize
- end
- raidmanager.getRemainingSpaces = function()
- local caps = {}
- local minSize = raidmanager.getMinCapacity()
- for i=2,#_G.driveServerTbl do --skip 1 since that's parity
- caps[i-1] = raidmanager.nextEmptySpace(i)
- end
- for i=1, #caps do
- caps[i] = minSize - caps[i]
- print(caps[i])
- end
- return caps
- end
- raidmanager.refreshParity = function()
- local minSize = raidmanager.getMinCapacity()
- for i=1,minSize do
- local thisByte = 0
- for j=2,#_G.driveServerTbl do
- thisByte = bit32.bxor(thisByte, sendReceive(_G.driveServerTbl[j], "readByte" , i))
- end
- network.send(_G.driveServerTbl[1], "writeByte", i, thisByte)
- end
- end
- raidmanager.readFile = function(driveDex, startLoc, userAddress)
- local drive =_G.driveServerTbl[driveDex]
- local currLoc = startLoc
- local nameSize = sendReceive(drive, "readByte", currLoc)
- currLoc = currLoc + 1
- local name = ""
- for i=0,nameSize-1 do
- name = name .. string.char(sendReceive(drive, "readByte", currLoc + i))
- end
- currLoc = currLoc + nameSize
- local address = ""
- for i=0, 15 do
- address = address .. string.char(sendReceive(drive, "readByte", currLoc + i))
- end
- currLoc = currLoc + 16
- local sizeSize = sendReceive(drive, "readByte", currLoc)
- currLoc = currLoc + 1
- local size = 0
- for i=0,sizeSize-1 do
- size = 256*size + sendReceive(drive, "readByte", currLoc+i)
- currLoc = currLoc + 1
- end
- local data = ""
- for i=0,size-1 do
- data = data .. string.char(sendReceive(drive, "readByte", currLoc + i))
- currLoc = currLoc + 1
- end
- if address == userAddress then
- return name, address, data, currLoc --next byte, useful for iterating
- else
- return "Not Allowed"
- end
- end
- raidmanager.findFile = function(name, address)
- local cap = raidmanager.getMinCapacity()
- for i=2,#_G.driveServerTbl do
- local drive =_G.driveServerTbl[i]
- local stop = raidmanager.nextEmptySpace(i)
- local loc = 1
- local last
- local curName
- local addr
- local found = false
- repeat
- last = loc
- curName, addr, _, loc = raidmanager.readFile(i, loc)
- if name == currName then
- found = true
- end
- until found or loc >= stop --shouldn't go above stop but better to avoid infinite loops
- if found then
- if addr == address then
- return drive,last --returning the drive index as well as the index of its position
- else
- return "Not allowed"
- end
- else
- return "Not found"
- end
- end
- end
- raidmanager.writeFile = function(name, address, data)
- --find a drive it will fit on
- local drive, dex = raidmanager.findFile(name, address)
- local driveDex = 0
- local currLoc = 0
- local insert = false
- if drive == "Not found" then
- local fullSize = raidmanager.getDataSize(name, address, data)
- if fullSize > raidmanager.getMinCapacity() then
- return 0
- end
- local caps = raidmanager.getRemainingSpaces()
- for i=1,#caps do
- if fullSize <= caps[i] then
- driveDex = i+1
- break
- end
- end
- elseif type(drive) == "number" then --found and allowed
- driveDex = drive
- currLoc = dex
- insert = true
- end
- if driveDex > 0 then
- if insert then
- --read current file, compare to this size, slide bytes around as needed
- end
- local drive =_G.driveServerTbl[driveDex]
- if currLoc == 0 then
- currLoc = raidmanager.nextEmptySpace(driveDex)
- end
- local nameSize = string.len(name)
- network.send(drive, "writeByte", currLoc, nameSize)
- currLoc = currLoc + 1
- for i=1,nameSize do
- network.send(drive, 12345, "writeByte", currLoc, string.byte(name, i))
- currLoc = currLoc + 1
- end
- for i=1,16 do
- network.send(drive, 12345, "writeByte", currLoc, string.byte(address, i))
- currLoc = currLoc + 1
- end
- local size = string.len(data)
- local sizeSize = math.ceil(math.log(1+size)/math.log(256))
- network.send(drive, writeByte, currLoc, sizeSize)
- currLoc = currLoc + 1
- local cur = size
- local mod
- for i=sizeSize-1,0,-1 do
- mod = cur % 256
- network.send(drive, "writeByte", currLoc + i, mod)
- cur = math.floor(cur / 256)
- end
- currLoc = currLoc + sizeSize
- for i=1,size do
- network.send(drive, "writeByte", currLoc, string.byte(data, i))
- currLoc = currLoc + 1
- end
- raidmanager.refreshParity()
- return drive, startLoc
- else
- return "No space"
- end
- end
- raidmanager.readByName = function(name, address)
- local drive, loc = raidmanager.findFile(name, address)
- if type(drive) == "string" then
- return drive --either "Not allowed" or "Not found", just pass it along
- end
- return raidmanager.readFile(drive, loc, address)
- end
- --appends data to end of existing file. Good for files too large to pass at once (particularly useful for use with networks)
- raidmanager.appendFile = function(name, address, newData)
- local driveOrMsg, dex = raidmanager.findFile(name, address)
- if type(driveOrMsg) == "string" then --"Not allowed" or "Not found"
- return driveOrMsg
- end
- local _, _, data, _ = raidmanager.readFile(driveOrMsg, dex, address)
- local data = oldData .. newData
- raidmanager.writeFile(name, address, data)
- end
- return raidmanager
- ::exit:: --if it can't load, no point trying to return anything
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement