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, }