You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
5.8 KiB
Lua
257 lines
5.8 KiB
Lua
local mt = {}
|
|
|
|
local function _new_block(v)
|
|
local block = {
|
|
value = v,
|
|
next = false,
|
|
prev = false,
|
|
}
|
|
return block
|
|
end
|
|
|
|
local endian_fmt = {
|
|
["little"] = "<",
|
|
["big"] = ">",
|
|
}
|
|
local function pack_data(data, header_len, endian)
|
|
local len = #data
|
|
local fmt = endian_fmt[endian] .. "I" .. header_len .. "c" .. (len)
|
|
return string.pack(fmt, len, data)
|
|
end
|
|
|
|
local function insert_free_list(self, block)
|
|
block.prev = false
|
|
block.next = false
|
|
local count = self.v_free_list_count
|
|
count = count + 1
|
|
self.v_free_list_count = count
|
|
self.v_free_list[count] = block
|
|
end
|
|
|
|
local function get_block(self)
|
|
local count = self.v_free_list_count
|
|
if count > 0 then
|
|
local block = self.v_free_list[count]
|
|
self.v_free_list[count] = nil
|
|
self.v_free_list_count = count - 1
|
|
return block
|
|
else
|
|
return _new_block()
|
|
end
|
|
end
|
|
|
|
local function free_block(self, block)
|
|
local next = block.next
|
|
local prev = block.prev
|
|
|
|
if next then
|
|
next.prev = prev
|
|
end
|
|
|
|
if prev then
|
|
prev.next = next
|
|
end
|
|
insert_free_list(self, block)
|
|
end
|
|
|
|
local function init_buffer(self)
|
|
local DEF_FREE_BLOCK = 3
|
|
|
|
for _ = 1, DEF_FREE_BLOCK do
|
|
insert_free_list(self, _new_block())
|
|
end
|
|
end
|
|
|
|
local function create()
|
|
local raw = {
|
|
v_free_list = {},
|
|
v_free_list_count = 0,
|
|
|
|
v_block_head = false,
|
|
v_block_tail = false,
|
|
v_size = 0,
|
|
}
|
|
|
|
init_buffer(raw)
|
|
setmetatable(raw, {
|
|
__index = mt,
|
|
})
|
|
return raw
|
|
end
|
|
|
|
function mt:push(data)
|
|
local block = get_block(self)
|
|
block.value = data
|
|
local size = self.v_size
|
|
self.v_size = size + #data
|
|
|
|
if not self.v_block_head then
|
|
assert(self.v_block_tail == false)
|
|
self.v_block_head = block
|
|
end
|
|
|
|
local tail = self.v_block_tail
|
|
if tail then
|
|
tail.next = block
|
|
block.prev = tail
|
|
block.next = false
|
|
end
|
|
|
|
self.v_block_tail = block
|
|
end
|
|
|
|
local buff = {}
|
|
function mt:look(nbytes)
|
|
local head = self.v_block_head
|
|
local size = self.v_size
|
|
if head and nbytes > 0 and size >= nbytes then
|
|
local count = 0
|
|
local index = 0
|
|
while head and count ~= nbytes do
|
|
local value = head.value
|
|
local len = #(value)
|
|
if count + len > nbytes then
|
|
local sub = nbytes - count
|
|
value = string.sub(value, 1, sub)
|
|
count = count + sub
|
|
else
|
|
count = count + len
|
|
end
|
|
index = index + 1
|
|
buff[index] = value
|
|
head = head.next
|
|
end
|
|
return table.concat(buff, "", 1, index)
|
|
end
|
|
return false
|
|
end
|
|
|
|
function mt:pop(nbytes)
|
|
local head = self.v_block_head
|
|
nbytes = nbytes or (head and #(head.value))
|
|
if head and nbytes > 0 and self.v_size >= nbytes then
|
|
local count = 0
|
|
local index = 0
|
|
while head and count ~= nbytes do
|
|
local len = #(head.value)
|
|
index = index + 1
|
|
if count + len > nbytes then
|
|
local sub = nbytes - count
|
|
local value = head.value
|
|
local sub_value = string.sub(value, 1, sub)
|
|
buff[index] = sub_value
|
|
count = count + sub
|
|
head.value = string.sub(value, sub + 1)
|
|
else
|
|
count = count + len
|
|
buff[index] = head.value
|
|
local next = head.next
|
|
free_block(self, head)
|
|
head = next
|
|
end
|
|
end
|
|
|
|
self.v_block_head = head
|
|
self.v_size = self.v_size - count
|
|
if not head then
|
|
self.v_block_tail = false
|
|
end
|
|
local ret = table.concat(buff, "", 1, index)
|
|
assert(#ret == nbytes)
|
|
return ret
|
|
end
|
|
return false
|
|
end
|
|
|
|
function mt:pop_all(out)
|
|
local v = self:pop()
|
|
local count = 0
|
|
|
|
while v do
|
|
count = count + 1
|
|
out[count] = v
|
|
v = self:pop()
|
|
end
|
|
return count
|
|
end
|
|
|
|
function mt:push_block(data, header_len, endian)
|
|
data = pack_data(data, header_len, endian)
|
|
self:push(data)
|
|
end
|
|
|
|
function mt:pop_block(header_len, endian)
|
|
if self.v_size > header_len then
|
|
local header = self:look(header_len)
|
|
local fmt = endian_fmt[endian] .. "I" .. header_len
|
|
-- local fmt = endian_fmt[endian].."H"
|
|
local len = string.unpack(fmt, header)
|
|
if self.v_size >= len + header_len then
|
|
self:pop(header_len)
|
|
return self:pop(len)
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function mt:pop_all_block(out, header_len, endian)
|
|
local v = self:pop_block(header_len, endian)
|
|
local count = 0
|
|
|
|
while v do
|
|
count = count + 1
|
|
out[count] = v
|
|
v = self:pop_block(header_len, endian)
|
|
end
|
|
return count
|
|
end
|
|
|
|
function mt:clear()
|
|
local head = self.v_block_head
|
|
while head do
|
|
insert_free_list(self, head)
|
|
head = head.next
|
|
end
|
|
self.v_block_head = false
|
|
self.v_block_tail = false
|
|
self.v_size = 0
|
|
end
|
|
|
|
function mt:get_head_data()
|
|
local head = self.v_block_head
|
|
return head and head.value or false
|
|
end
|
|
|
|
---- for test
|
|
local function _dump_list(list)
|
|
while list do
|
|
print(" node = ", list)
|
|
for k, v in pairs(list) do
|
|
print(" ", k, v)
|
|
end
|
|
print("----------")
|
|
list = list.next
|
|
end
|
|
end
|
|
|
|
function mt:dump()
|
|
print("==== meta info ====")
|
|
for k, v in pairs(self) do
|
|
print(k, v)
|
|
end
|
|
|
|
print("===== free list ====", self.v_free_list_count)
|
|
local free_list = self.v_free_list
|
|
for k, v in pairs(free_list) do
|
|
print(k, v)
|
|
end
|
|
|
|
print("===== head list ====")
|
|
_dump_list(self.v_block_head)
|
|
end
|
|
|
|
return {
|
|
create = create,
|
|
pack_data = pack_data,
|
|
}
|