I'm sure bitser is causing love2d to close

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Gunroar:Cannon()
Party member
Posts: 1144
Joined: Thu Dec 10, 2020 1:57 am

I'm sure bitser is causing love2d to close

Post by Gunroar:Cannon() »

Some times my app just stops running, and lately it won't even start every second time. I tracked down an debugged suspected bitser. Then lo and behold, if I made toybox.saveData() (a function that gets called to ... save data) it doesn't close anymore.



I've only tested this on love for android though that's still a problem. Any thing known in bitser that can cause this or any other alternatives (I can't lazily save data due to large tables that reference each other and local value limit , and don't need save functions or userdata I think).

Here's snippets of the code I use because I'm sure no one will appreciate me uploading 30mb of code.

Code: Select all

local SETTINGS_FILE = "settings.dat"

local function getSettingsToLoad()
    if love.filesystem.isFile(SETTINGS_FILE)
    and (love.filesystem.newFile(SETTINGS_FILE):getSize()>0) then
        return-- love.filesystem.load(SETTINGS_FILE,"r")()--
        bitser.loadLoveFile(SETTINGS_FILE, "r") 
    end
    return {}
end

local function getSettingsToSave(data)
    local tmp = love.filesystem.newFile(string.format("%s_TMP",SETTINGS_FILE),"w")
    tmp:write(data)
    local value = bitser.loadLoveFile(string.format("%s_TMP",SETTINGS_FILE), "r")
    
    if type(value) == "table" then
        local file = love.filesystem.newFile(SETTINGS_FILE, "w")
        file:write(data)
        return true
    elseif nil then--game then--.map then
        GTimer:after(3,function() gooi.alert({
            text = "No space to save data!"
        })
        end)
    end
    
end

LGML.getColor = function(col,alpha)
    if type(col) == "table" then 
        if alpha then
            col = {col[1],col[2],col[3]}
            col[4] = alpha
        end
        return col
    end
    local col = colors[col] or colors.red
    col = {col[1],col[2],col[3],col[4]}
    if alpha then
        col[4] = alpha
    end
    return col
end

LGML._data = {}
LGML.getData = function(name)
    SETTINGS_FILE = name or "settings.dat"
    local d = LGML._data[name] or getSettingsToLoad() or {}
    LGML._data[name] = d
    return d
end

function LGML.saveData(name)
    SETTINGS_FILE = name or "settings.dat"
    getSettingsToSave(bitser.dumps(LGML._data[name]))--saveTable(LGML._data[name] or getSettingsToLoad()))
end

Code: Select all

local floor = math.floor
local pairs = pairs
local type = type
local insert = table.insert
local getmetatable = getmetatable
local setmetatable = setmetatable

local ffi = require("ffi")
local buf_pos = 0
local buf_size = -1
local buf = nil
local writable_buf = nil
local writable_buf_size = nil

local function Buffer_prereserve(min_size)
	if buf_size < min_size then
		buf_size = min_size
		buf = ffi.new("uint8_t[?]", buf_size)
	end
end

local function Buffer_clear()
	buf_size = -1
	buf = nil
	writable_buf = nil
	writable_buf_size = nil
end

local function Buffer_makeBuffer(size)
	if writable_buf then
		buf = writable_buf
		buf_size = writable_buf_size
		writable_buf = nil
		writable_buf_size = nil
	end
	buf_pos = 0
	Buffer_prereserve(size)
end

local function Buffer_newReader(str)
	Buffer_makeBuffer(#str)
	ffi.copy(buf, str, #str)
end

local function Buffer_newDataReader(data, size)
	writable_buf = buf
	writable_buf_size = buf_size
	buf_pos = 0
	buf_size = size
	buf = ffi.cast("uint8_t*", data)
end

local function Buffer_reserve(additional_size)
    log("[Bitser] Buffer reserve")
	while buf_pos + additional_size > buf_size do
	    log("hmmmmm")
		buf_size = buf_size * 2
		local oldbuf = buf
		buf = ffi.new("uint8_t[?]", buf_size)
		ffi.copy(buf, oldbuf, buf_pos)
	end
end

local function Buffer_write_byte(x)
	Buffer_reserve(1)
	buf[buf_pos] = x
	buf_pos = buf_pos + 1
end

local function Buffer_write_string(s)
	Buffer_reserve(#s)
	ffi.copy(buf + buf_pos, s, #s)
	buf_pos = buf_pos + #s
end

local function Buffer_write_data(ct, len, ...)
	Buffer_reserve(len)
	ffi.copy(buf + buf_pos, ffi.new(ct, ...), len)
	buf_pos = buf_pos + len
end

local function Buffer_ensure(numbytes)
	if buf_pos + numbytes > buf_size then
		return--error("malformed serialized data")
	end
	
	return true
end

local function Buffer_read_byte()
	if not Buffer_ensure(1) then 
	    return error()
	end    
	local x = buf[buf_pos]
	buf_pos = buf_pos + 1
	return x
end

local function Buffer_read_string(len)
	Buffer_ensure(len)
	local x = ffi.string(buf + buf_pos, len)
	buf_pos = buf_pos + len
	return x
end

local function Buffer_read_data(ct, len)
	Buffer_ensure(len)
	local x = ffi.new(ct)
	ffi.copy(x, buf + buf_pos, len)
	buf_pos = buf_pos + len
	return x
end

local resource_registry = {}
local resource_name_registry = {}
local class_registry = {}
local class_name_registry = {}
local classkey_registry = {}
local class_deserialize_registry = {}

local serialize_value

local function write_number(value, _)
	if floor(value) == value and value >= -2147483648 and value <= 2147483647 then
		if value >= -27 and value <= 100 then
			--small int
			Buffer_write_byte(value + 27)
		elseif value >= -32768 and value <= 32767 then
			--short int
			Buffer_write_byte(250)
			Buffer_write_data("int16_t[1]", 2, value)
		else
			--long int
			Buffer_write_byte(245)
			Buffer_write_data("int32_t[1]", 4, value)
		end
	else
		--double
		Buffer_write_byte(246)
		Buffer_write_data("double[1]", 8, value)
	end
end

local function write_string(value, _)
	if #value < 32 then
		--short string
		Buffer_write_byte(192 + #value)
	else
		--long string
		Buffer_write_byte(244)
		write_number(#value)
	end
	Buffer_write_string(value)
end

local function write_nil(_, _)
	Buffer_write_byte(247)
end

local function write_boolean(value, _)
	Buffer_write_byte(value and 249 or 248)
end

local function write_table(value, seen)
	local classkey
	local classname = (class_name_registry[value.class] -- MiddleClass
		or class_name_registry[value.__baseclass] -- SECL
		or class_name_registry[getmetatable(value)] -- hump.class
		or class_name_registry[value.__class__]) -- Slither
	if classname then
		classkey = classkey_registry[classname]
		Buffer_write_byte(242)
		serialize_value(classname, seen)
	else
		Buffer_write_byte(240)
	end
	local len = #value
	write_number(len, seen)
	for i = 1, len do
		serialize_value(value[i], seen)
	end
	local klen = 0
	for k in pairs(value) do
		if (type(k) ~= 'number' or floor(k) ~= k or k > len or k < 1) and k ~= classkey then
			klen = klen + 1
		end
	end
	write_number(klen, seen)
	for k, v in pairs(value) do
		if (type(k) ~= 'number' or floor(k) ~= k or k > len or k < 1) and k ~= classkey then
			serialize_value(k, seen)
			serialize_value(v, seen)
		end
	end
end

local types = {number = write_number, string = write_string, table = write_table, boolean = write_boolean, ["nil"] = write_nil}

serialize_value = function(value, seen)
	if seen[value] then
		local ref = seen[value]
		if ref < 64 then
			--small reference
			Buffer_write_byte(128 + ref)
		else
			--long reference
			Buffer_write_byte(243)
			write_number(ref, seen)
		end
		return
	end
	local t = type(value)
	if t ~= 'number' and t ~= 'boolean' and t ~= 'nil' then
		seen[value] = seen.len
		seen.len = seen.len + 1
	end
	if resource_name_registry[value] then
		local name = resource_name_registry[value]
		if #name < 16 then
			--small resource
			Buffer_write_byte(224 + #name)
			Buffer_write_string(name)
		else
			--long resource
			Buffer_write_byte(241)
			write_string(name, seen)
		end
		return
	end
	(types[t] or
		error("cannot serialize type " .. t)
		)(value, seen)
end

local function serialize(value)
	Buffer_makeBuffer(4096)
	local seen = {len = 0}
	serialize_value(value, seen)
end

local function add_to_seen(value, seen)
	insert(seen, value)
	return value
end

local function reserve_seen(seen)
	insert(seen, 42)
	return #seen
end

local function deserialize_value(seen)
	local t = Buffer_read_byte()
	if t < 128 then
		--small int
		return t - 27
	elseif t < 192 then
		--small reference
		return seen[t - 127]
	elseif t < 224 then
		--small string
		return add_to_seen(Buffer_read_string(t - 192), seen)
	elseif t < 240 then
		--small resource
		return add_to_seen(resource_registry[Buffer_read_string(t - 224)], seen)
	elseif t == 240 then
		--table
		local v = add_to_seen({}, seen)
		local len = deserialize_value(seen)
		for i = 1, len do
			v[i] = deserialize_value(seen)
		end
		len = deserialize_value(seen)
		for _ = 1, len do
			local key = deserialize_value(seen)
			v[key] = deserialize_value(seen)
		end
		return v
	elseif t == 241 then
		--long resource
		local idx = reserve_seen(seen)
		local value = resource_registry[deserialize_value(seen)]
		seen[idx] = value
		return value
	elseif t == 242 then
		--instance
		local instance = add_to_seen({}, seen)
		local classname = deserialize_value(seen)
		local class = class_registry[classname]
		local classkey = classkey_registry[classname]
		local deserializer = class_deserialize_registry[classname]
		local len = deserialize_value(seen)
		for i = 1, len do
			instance[i] = deserialize_value(seen)
		end
		len = deserialize_value(seen)
		for _ = 1, len do
			local key = deserialize_value(seen)
			instance[key] = deserialize_value(seen)
		end
		if classkey then
			instance[classkey] = class
		end
		return deserializer(instance, class)
	elseif t == 243 then
		--reference
		return seen[deserialize_value(seen) + 1]
	elseif t == 244 then
		--long string
		return add_to_seen(Buffer_read_string(deserialize_value(seen)), seen)
	elseif t == 245 then
		--long int
		return Buffer_read_data("int32_t[1]", 4)[0]
	elseif t == 246 then
		--double
		return Buffer_read_data("double[1]", 8)[0]
	elseif t == 247 then
		--nil
		return nil
	elseif t == 248 then
		--false
		return false
	elseif t == 249 then
		--true
		return true
	elseif t == 250 then
		--short int
		return Buffer_read_data("int16_t[1]", 2)[0]
	else
		return nil-- error("unsupported serialized type " .. t)
	end
end

local function deserialize_MiddleClass(instance, class)
	return setmetatable(instance, class.__instanceDict)
end

local function deserialize_SECL(instance, class)
	return setmetatable(instance, getmetatable(class))
end

local deserialize_humpclass = setmetatable

local function deserialize_Slither(instance, class)
	return getmetatable(class).allocate(instance)
end

return {dumps = function(value)
	serialize(value)
	return ffi.string(buf, buf_pos)
end, dumpLoveFile = function(fname, value)
	serialize(value)
	love.filesystem.write(fname, ffi.string(buf, buf_pos))
end, loadLoveFile = function(fname)
	local serializedData = love.filesystem.newFileData(fname)
	Buffer_newDataReader(serializedData:getPointer(), serializedData:getSize())
	return deserialize_value({})
end, loadData = function(data, size)
	Buffer_newDataReader(data, size)
	return deserialize_value({})
end, loads = function(str)
	Buffer_newReader(str)
	return deserialize_value({})
end, register = function(name, resource)
	assert(not resource_registry[name], name .. " already registered")
	resource_registry[name] = resource
	resource_name_registry[resource] = name
	return resource
end, unregister = function(name)
	resource_name_registry[resource_registry[name]] = nil
	resource_registry[name] = nil
end, registerClass = function(name, class, classkey, deserializer)
	if not class then
		class = name
		name = class.__name__ or class.name
	end
	if not classkey then
		if class.__instanceDict then
			-- assume MiddleClass
			classkey = 'class'
		elseif class.__baseclass then
			-- assume SECL
			classkey = '__baseclass'
		end
		-- assume hump.class, Slither, or something else that doesn't store the
		-- class directly on the instance
	end
	if not deserializer then
		if class.__instanceDict then
			-- assume MiddleClass
			deserializer = deserialize_MiddleClass
		elseif class.__baseclass then
			-- assume SECL
			deserializer = deserialize_SECL
		elseif class.__index == class then
			-- assume hump.class
			deserializer = deserialize_humpclass
		elseif class.__name__ then
			-- assume Slither
			deserializer = deserialize_Slither
		else
			error("no deserializer given for unsupported class library")
		end
	end
	class_registry[name] = class
	classkey_registry[name] = classkey
	class_deserialize_registry[name] = deserializer
	class_name_registry[class] = name
	return class
end, unregisterClass = function(name)
	class_name_registry[class_registry[name]] = nil
	classkey_registry[name] = nil
	class_deserialize_registry[name] = nil
	class_registry[name] = nil
end, reserveBuffer = Buffer_prereserve, clearBuffer = Buffer_clear}
No,it's not the log(). Ffi maybe?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
pgimeno
Party member
Posts: 3691
Joined: Sun Oct 18, 2015 2:58 pm

Re: I'm sure bitser is causing love2d to close

Post by pgimeno »

Could it be running out of memory? Have you tried adding a log() at the end of Buffer_reserve() to verify that it returns?
User avatar
Gunroar:Cannon()
Party member
Posts: 1144
Joined: Thu Dec 10, 2020 1:57 am

Re: I'm sure bitser is causing love2d to close

Post by Gunroar:Cannon() »

But Bitser_reserve doesn't return anything? I still did it and it said "done" but when it closes it doesn't log anything (I think).
At least I can kind of confirm it's due to RAM, like you said, because I couldn't get it to close every second time any more so I opened a bunch of apps and it started happening again (then stopped happening, it's kind of inconsistent).

Is there a way I can fix this or maybe another saving lib that doesn't have this same problem?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
Gunroar:Cannon()
Party member
Posts: 1144
Joined: Thu Dec 10, 2020 1:57 am

Re: I'm sure bitser is causing love2d to close

Post by Gunroar:Cannon() »

Update: I tried binser and it looked liked it serialized it well with a long weird encoded list of %$$$gamedata...etc You know, serialized stuff. But when I deserialize it I get {27} :(

Now I'm using lume.serialize but it serializes in lua table and I don't think it supports tables tat reference themselves.
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
pgimeno
Party member
Posts: 3691
Joined: Sun Oct 18, 2015 2:58 pm

Re: I'm sure bitser is causing love2d to close

Post by pgimeno »

But what do you need that buffer for in the first place? Edit: Wait, that's the source of bitser as modified by you? Go get a more recent one, there are some crash fixes.
User avatar
Gunroar:Cannon()
Party member
Posts: 1144
Joined: Thu Dec 10, 2020 1:57 am

Re: I'm sure bitser is causing love2d to close

Post by Gunroar:Cannon() »

Oh, okay. And errr...what do you mean by buffer. The encoded text?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
pgimeno
Party member
Posts: 3691
Joined: Sun Oct 18, 2015 2:58 pm

Re: I'm sure bitser is causing love2d to close

Post by pgimeno »

Nah, I didn't realize it was the source for bitser. I meant the buffer that bitser itself allocates in Buffer_reserve, which I thought was your code. When I realized my mistake I stroke it through.
User avatar
Gunroar:Cannon()
Party member
Posts: 1144
Joined: Thu Dec 10, 2020 1:57 am

Re: I'm sure bitser is causing love2d to close

Post by Gunroar:Cannon() »

pgimeno wrote: Sun Dec 18, 2022 1:57 pm Nah, I didn't realize it was the source for bitser. I meant the buffer that bitser itself allocates in Buffer_reserve, which I thought was your code. When I realized my mistake I stroke it through.
Yeah. Okay. Thanks for the help :awesome: It works now. And I decided to keep the simple lume serialize option (in my "engine") as bitser is a slower in saving. So I can switch between more advanced and simple serialization between projects :crazy:
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
Post Reply

Who is online

Users browsing this forum: No registered users and 9 guests