488 lines
12 KiB
GDScript
488 lines
12 KiB
GDScript
# Copyright (C) 2019 Tintin Ho
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
#
|
|
#
|
|
# godot-msgpack
|
|
#
|
|
# This is a MessagePack serializer written in pure GDSciprt. To install this
|
|
# library in your project simply and copy and paste this file inside your
|
|
# project (e.g. res://msgpack.gd).
|
|
#
|
|
#
|
|
# class Msgpack
|
|
# static Dictionary encode(Variant value)
|
|
# Convert a value (number, string, array and dictionary) into their
|
|
# counterparts in messagepack. Returns dictionary with three fields:
|
|
# `result` which is the packed data (a PackedByteArray); `error` which is the
|
|
# error code; and `error_string` which is a human readable error message
|
|
#
|
|
# static Dictionary decode(PackedByteArray bytes)
|
|
# Convert a packed data (a PackedByteArray) into a value, the reverse of the
|
|
# encode function. The return value is similar to the one in the encode
|
|
# method
|
|
|
|
static func encode(value, buffer: StreamPeerBuffer):
|
|
var ctx = {error = OK, error_string = ""}
|
|
buffer.big_endian = true
|
|
|
|
_encode(buffer, value, ctx)
|
|
if ctx.error == OK:
|
|
return {
|
|
result = buffer.data_array,
|
|
error = OK,
|
|
error_string = "",
|
|
}
|
|
else:
|
|
return {
|
|
result = PackedByteArray(),
|
|
error = ctx.error,
|
|
error_string = ctx.error_string,
|
|
}
|
|
|
|
static func decode(buffer: StreamPeerBuffer):
|
|
buffer.big_endian = true
|
|
|
|
var ctx = {error = OK, error_string = ""}
|
|
var value = _decode(buffer, ctx)
|
|
if ctx.error == OK:
|
|
if buffer.get_position() == buffer.get_size():
|
|
return {result = value, error = OK, error_string = ""}
|
|
else:
|
|
var msg = "excess buffer %s bytes" % [buffer.get_size() - buffer.get_position()]
|
|
return {result = null, error = FAILED, error_string = msg}
|
|
else:
|
|
return {result = null, error = ctx.error, error_string = ctx.error_string}
|
|
|
|
static func _encode(buf, value, ctx):
|
|
match typeof(value):
|
|
TYPE_NIL:
|
|
buf.put_u8(0xc0)
|
|
|
|
TYPE_BOOL:
|
|
if value:
|
|
buf.put_u8(0xc3)
|
|
else:
|
|
buf.put_u8(0xc2)
|
|
|
|
TYPE_INT:
|
|
if -(1 << 5) <= value and value <= (1 << 7) - 1:
|
|
# fixnum (positive and negative)
|
|
buf.put_8(value)
|
|
elif -(1 << 7) <= value and value <= (1 << 7):
|
|
buf.put_u8(0xd0)
|
|
buf.put_8(value)
|
|
elif -(1 << 15) <= value and value <= (1 << 15):
|
|
buf.put_u8(0xd1)
|
|
buf.put_16(value)
|
|
elif -(1 << 31) <= value and value <= (1 << 31):
|
|
buf.put_u8(0xd2)
|
|
buf.put_32(value)
|
|
else:
|
|
buf.put_u8(0xd3)
|
|
buf.put_64(value)
|
|
|
|
TYPE_FLOAT:
|
|
buf.put_u8(0xcb)
|
|
buf.put_double(value)
|
|
|
|
TYPE_STRING:
|
|
var bytes = value.to_utf8_buffer()
|
|
|
|
var size = bytes.size()
|
|
if size <= (1 << 5) - 1:
|
|
# type fixstr [101XXXXX]
|
|
buf.put_u8(0xa0 | size)
|
|
elif size <= (1 << 8) - 1:
|
|
# type str 8
|
|
buf.put_u8(0xd9)
|
|
buf.put_u8(size)
|
|
elif size <= (1 << 16) - 1:
|
|
# type str 16
|
|
buf.put_u8(0xda)
|
|
buf.put_u16(size)
|
|
elif size <= (1 << 32) - 1:
|
|
# type str 32
|
|
buf.put_u8(0xdb)
|
|
buf.put_u32(size)
|
|
else:
|
|
assert(false)
|
|
|
|
buf.put_data(bytes)
|
|
|
|
TYPE_PACKED_BYTE_ARRAY:
|
|
var size = value.size()
|
|
if size <= (1 << 8) - 1:
|
|
buf.put_u8(0xc4)
|
|
buf.put_u8(size)
|
|
elif size <= (1 << 16) - 1:
|
|
buf.put_u8(0xc5)
|
|
buf.put_u16(size)
|
|
elif size <= (1 << 32) - 1:
|
|
buf.put_u8(0xc6)
|
|
buf.put_u32(size)
|
|
else:
|
|
assert(false)
|
|
|
|
buf.put_data(value)
|
|
|
|
TYPE_ARRAY:
|
|
var size = value.size()
|
|
if size <= 15:
|
|
# type fixarray [1001XXXX]
|
|
buf.put_u8(0x90 | size)
|
|
elif size <= (1 << 16) - 1:
|
|
# type array 16
|
|
buf.put_u8(0xdc)
|
|
buf.put_u16(size)
|
|
elif size <= (1 << 32) - 1:
|
|
# type array 32
|
|
buf.put_u8(0xdd)
|
|
buf.put_u32(size)
|
|
else:
|
|
assert(false)
|
|
|
|
for obj in value:
|
|
_encode(buf, obj, ctx)
|
|
if ctx.error != OK:
|
|
return
|
|
|
|
TYPE_DICTIONARY:
|
|
var size = value.size()
|
|
if size <= 15:
|
|
# type fixmap [1000XXXX]
|
|
buf.put_u8(0x80 | size)
|
|
elif size <= (1 << 16) - 1:
|
|
# type map 16
|
|
buf.put_u8(0xde)
|
|
buf.put_u16(size)
|
|
elif size <= (1 << 32) - 1:
|
|
# type map 32
|
|
buf.put_u8(0xdf)
|
|
buf.put_u32(size)
|
|
else:
|
|
assert(false)
|
|
|
|
for key in value:
|
|
_encode(buf, key, ctx)
|
|
if ctx.error != OK:
|
|
return
|
|
|
|
_encode(buf, value[key], ctx)
|
|
if ctx.error != OK:
|
|
return
|
|
_:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "unsupported data type %s" % [typeof(value)]
|
|
|
|
static func _decode(buffer, ctx):
|
|
if buffer.get_position() == buffer.get_size():
|
|
ctx.error = FAILED
|
|
ctx.error_string = "unexpected end of input"
|
|
return null
|
|
|
|
var head = buffer.get_u8()
|
|
if head == 0xc0:
|
|
return null
|
|
elif head == 0xc2:
|
|
return false
|
|
elif head == 0xc3:
|
|
return true
|
|
|
|
# Integers
|
|
elif head & 0x80 == 0:
|
|
# positive fixnum
|
|
return head
|
|
elif (~head) & 0xe0 == 0:
|
|
# negative fixnum
|
|
return head - 256
|
|
elif head == 0xcc:
|
|
# uint 8
|
|
if buffer.get_size() - buffer.get_position() < 1:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for uint8"
|
|
return null
|
|
|
|
return buffer.get_u8()
|
|
elif head == 0xcd:
|
|
# uint 16
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for uint16"
|
|
return null
|
|
|
|
return buffer.get_u16()
|
|
elif head == 0xce:
|
|
# uint 32
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for uint32"
|
|
return null
|
|
|
|
return buffer.get_u32()
|
|
elif head == 0xcf:
|
|
# uint 64
|
|
if buffer.get_size() - buffer.get_position() < 8:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for uint64"
|
|
return null
|
|
|
|
return buffer.get_u64()
|
|
elif head == 0xd0:
|
|
# int 8
|
|
if buffer.get_size() - buffer.get_position() < 1:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enogh buffer for int8"
|
|
return null
|
|
|
|
return buffer.get_8()
|
|
elif head == 0xd1:
|
|
# int 16
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enogh buffer for int16"
|
|
return null
|
|
|
|
return buffer.get_16()
|
|
elif head == 0xd2:
|
|
# int 32
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for int32"
|
|
return null
|
|
|
|
return buffer.get_32()
|
|
elif head == 0xd3:
|
|
# int 64
|
|
if buffer.get_size() - buffer.get_position() < 8:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for int64"
|
|
return null
|
|
|
|
return buffer.get_64()
|
|
|
|
# Float
|
|
elif head == 0xca:
|
|
# float32
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for float32"
|
|
return null
|
|
|
|
return buffer.get_float()
|
|
elif head == 0xcb:
|
|
# float64
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for float64"
|
|
return null
|
|
|
|
return buffer.get_double()
|
|
|
|
# String
|
|
elif (~head) & 0xa0 == 0:
|
|
var size = head & 0x1f
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for fixstr required %s bytes" % [size]
|
|
return null
|
|
|
|
return buffer.get_utf8_string(size)
|
|
elif head == 0xd9:
|
|
if buffer.get_size() - buffer.get_position() < 1:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str8 size"
|
|
return null
|
|
|
|
var size = buffer.get_u8()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str8 data required %s bytes" % [size]
|
|
return null
|
|
|
|
return buffer.get_utf8_string(size)
|
|
elif head == 0xda:
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str16 size"
|
|
return null
|
|
|
|
var size = buffer.get_u16()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str16 data required %s bytes" % [size]
|
|
return null
|
|
|
|
return buffer.get_utf8_string(size)
|
|
elif head == 0xdb:
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str32 size"
|
|
return null
|
|
|
|
var size = buffer.get_u32()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for str32 data required %s bytes" % [size]
|
|
return null
|
|
|
|
return buffer.get_utf8_string(size)
|
|
|
|
# Binary
|
|
elif head == 0xc4:
|
|
if buffer.get_size() - buffer.get_position() < 1:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin8 size"
|
|
return null
|
|
|
|
var size = buffer.get_u8()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin8 data required %s bytes" % [size]
|
|
return null
|
|
|
|
var res = buffer.get_data(size)
|
|
assert(res[0] == OK)
|
|
return res[1]
|
|
elif head == 0xc5:
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin16 size"
|
|
return null
|
|
|
|
var size = buffer.get_u16()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin16 data required %s bytes" % [size]
|
|
return null
|
|
|
|
var res = buffer.get_data(size)
|
|
assert(res[0] == OK)
|
|
return res[1]
|
|
elif head == 0xc6:
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin32 size"
|
|
return null
|
|
|
|
var size = buffer.get_u32()
|
|
if buffer.get_size() - buffer.get_position() < size:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for bin32 data required %s bytes" % [size]
|
|
return null
|
|
|
|
var res = buffer.get_data(size)
|
|
assert(res[0] == OK)
|
|
return res[1]
|
|
|
|
# Array
|
|
elif head & 0xf0 == 0x90:
|
|
var size = head & 0x0f
|
|
var res = []
|
|
for i in range(size):
|
|
res.append(_decode(buffer, ctx))
|
|
if ctx.error != OK:
|
|
return null
|
|
return res
|
|
elif head == 0xdc:
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for array16 size"
|
|
return null
|
|
|
|
var size = buffer.get_u16()
|
|
var res = []
|
|
for i in range(size):
|
|
res.append(_decode(buffer, ctx))
|
|
if ctx.error != OK:
|
|
return null
|
|
return res
|
|
elif head == 0xdd:
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for array32 size"
|
|
return null
|
|
|
|
var size = buffer.get_u32()
|
|
var res = []
|
|
for i in range(size):
|
|
res.append(_decode(buffer, ctx))
|
|
if ctx.error != OK:
|
|
return null
|
|
return res
|
|
|
|
# Map
|
|
elif head & 0xf0 == 0x80:
|
|
var size = head & 0x0f
|
|
var res = {}
|
|
for i in range(size):
|
|
var k = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
var v = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
res[k] = v
|
|
return res
|
|
elif head == 0xde:
|
|
if buffer.get_size() - buffer.get_position() < 2:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for map16 size"
|
|
return null
|
|
|
|
var size = buffer.get_u16()
|
|
var res = {}
|
|
for i in range(size):
|
|
var k = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
var v = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
res[k] = v
|
|
return res
|
|
elif head == 0xdf:
|
|
if buffer.get_size() - buffer.get_position() < 4:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "not enough buffer for map32 size"
|
|
return null
|
|
|
|
var size = buffer.get_u32()
|
|
var res = {}
|
|
for i in range(size):
|
|
var k = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
var v = _decode(buffer, ctx)
|
|
if ctx.error != OK:
|
|
return null
|
|
|
|
res[k] = v
|
|
return res
|
|
|
|
else:
|
|
ctx.error = FAILED
|
|
ctx.error_string = "invalid byte tag %02X at pos %s" % [head, buffer.get_position()]
|
|
return null
|