Fastest Text Compression (ZSTD)

Roblox new EncodingService:CompressBuffer are way better, you should use it anyway! it had level configs in it!

For datastore, uses an custom base94 safe for json format




Old post:

Creator Store | Rbxm | Github

Fastest and best general text compression literally.
ZStandard, but without any configurations. Also this is not a pure luau implementation.

Since it compressed to raw binary data, so it won’t work for datastore. However, you can ecode it to base64 or custom base93 for it to work.


Credits
Cons
  • No access to any configurations to this ZSTD.
  • Exploiting Roblox APIs to achiving such ZSTD.
8 Likes

Just a few minutes before this post was made, I’ve written a very simple wrapper for this and was surprised to see a notification about this right after I have finished it :sob:

local HttpService = game:GetService("HttpService")
local Base64 = require(script.Parent.Base64)
local Zstd = {}

Zstd.Compress = function(String)
	local Compressed = buffer.fromstring(String)
	local Encoded = HttpService:JSONEncode(Compressed)
	local zB64 = Encoded:match('"zbase64":"(.-)"')
	local B64 = Encoded:match('"base64":"(.-)"')
	
	if B64 then
		return error("String passed is too short to compress.")
	end
	
	if zB64 then
		return buffer.tostring(Base64.decode(buffer.fromstring(zB64)))
	end
	
	return error("Failed to utilize Roblox's internal zstd compression algorithm by JSONEncoding a buffer. If you get this error, this feature may not be removed.")
end

Zstd.Decompress = function(String)
	local Encoded = Base64.encode(buffer.fromstring(String))
	local ReconstructedJSON = [[{"m": null, "t": "buffer", "zbase64": "]] .. buffer.tostring(Encoded) .. [["}]]
	local OriginalBuffer = HttpService:JSONDecode(ReconstructedJSON)
	
	return buffer.tostring(OriginalBuffer)
end

return Zstd
3 Likes

Amazing resource! Just wanted to notify you that the creator store install is broken and cant be used to download the resource.

1 Like

You should use my base64 :wink:

Also if anyone wants to make a pure luau ZSTD this website seems to be able to decompress it: Omni ToolBox--Online Zstd Compression which uses GitHub - OneIdentity/zstd-js

3 Likes

How does this compare against Base64/Base64.lua at master · Reselim/Base64 · GitHub?

The bench called ‘Old’ is actually that

1 Like

What are the use cases with networking for example sending a string back and forth from the client and server?

I know there are libraries that do their best to try to reduce the packet size with types like strings and also (I assume this uses buffers) uses a buffer system to accomplish this.

FYI - Roblox implicitly compresses buffers using ZSTD behind the scenes when you send them over the wire (remotes, http service, etc.), or at rest (data stores, memory stores). No Luau implementation is going to beat the natively compiled ZSTD operation.

So you can do:

local myStr = "Pretend this is a much longer string..."
local buf = buffer.fromstring(myStr)

-- Examples:

-- "buf" is compressed over the network automatically:
remote:FireAllClients(buf)

-- "buf" is decompressed automatically:
remote.OnServerEvent:Connect(function(buf)
end)

-- "buf" will be compressed in the datastore:
dataStore:SetAsync("key", buf)

-- "buf" will be automatically decompressed:
buf = dataStore:GetAsync("key")

Note: Roblox might compress it, but not always. It uses whatever is best. For small data, often the uncompressed data is smaller.

10 Likes

I use this in very specific and rare cases, typically when I need to send extremely large serialized strings, such as massive Vector3 of data from the server to the client in Roblox. This kind of data transfer can be bandwidth-heavy, and every byte matters. Also the main goal of using this just to replace other old text compression people still use nowadays.
But yea, there are no use cases I found for client to server

the use case for this most likely: 10,000+ Physical NPCs running with ~60 fps on low-end device

1 Like

While I was aware that Roblox automatically compresses buffer data when saving to DataStores, but I honestly didn’t know that the same applies to network. Thank you for pointing that out, I really appreciate it

1 Like

Mann, I really thought this was a ZSTD implementation, but its just a base64 encode/decode :sob: :sob:

I really want compression with zstd rn, not just base64.. I thought I found a perfect library lol.. Oh well, its fine ig..

Maybe rename the post and don’t state that its ZSTD when its just a base64 encoder/decoder :frowning:

I might try and make my own or keep looking for someone who made a proper ZSTD compression/decompression library in pure lua. Worst case I might transcode the original and official library: GitHub - facebook/zstd: Zstandard - Fast real-time compression algorithm .-.

I’m not sure what you’re talking about
My version acts mostly like real ZSTD compression by using Roblox APIs that return a base64-encoded ZSTD result, then decode it. It works just like ZSTD, but sadly, you can’t change any settings like compression levels like the real one

I think you’re misunderstanding because most of the code is just base64 encode/decode

Trust me, if you’re thinking of writing ZSTD in pure Lua, it’s not worth the effort

I found out, it has to be a certain size for this method to actually compress it..

Yeah, so i dont see the reason why its not worth it to create a Luau implementation, especially one that supports compression levels

Worth the results in compression ratio but not worth the time to make since you need to port FSE, Huff0,… and it could be very very slow because of luau doesn’t have c built in functions, that need to be reimplemented in luau, and insane amount of cpu usage as same as memory. Even worse, a possible crash.
Luau implementation could be possible but really difficult to do, that would be pretty impressive to be ported by someonez

I can’t see any sane mind creating a non !native port for the sake of adding client support. --!native scripts run on the bare metal. And yes, porting it would be incredibly difficult but i say there are rewards to be reaped for that for sure

--!native doesn’t give a magically huge speed boost. It’s like taking something already slow and making it only slightly less slow. Even with buffer-based code, the main performance downgrade usually comes from algorithm complexity, frequent function calls, memory access overhead, and heavy branching inside hot loops, not from the lack of --!native