Files

488 lines
12 KiB
GDScript3
Raw Permalink Normal View History

# 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