Added binary read/write functions, still untested

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-10-27 21:22:09 +01:00
parent d04e0821d1
commit bda76cd189
6 changed files with 486 additions and 146 deletions
+40
View File
@@ -80,8 +80,12 @@ PureBasic include for creating/reading MessagePack data.
* `#MsgPack_Error_AtOrPastTheEndOfBuffer`
* `#MsgPack_Error_ReadPastEndOfBuffer`
* `#MsgPack_Error_InvalidFormatCode`
* `#MsgPack_Error_FailedToAllocateBuffer`
* `#MsgPack_Error_BinaryDataSizeIsNegative`
* `#MsgPack_Error_BinaryDataSizeIsTooBig`
* `#MsgPack_Error_OutputBufferTooSmall`
* `#MsgPack_Error_UnsupportedStringEncoding`
* `#MsgPack_Error_InvalidStringSize`
* `#MsgPack_Error_CannotGrowBufferDueToConfig`
* `.MsgPack_DataFlag` \
@@ -127,6 +131,42 @@ PureBasic include for creating/reading MessagePack data.
Returns `#MsgPack_Error_Success` on success, something else otherwise.
### Binary
* `MsgPackWriteBinary.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
Writes `BufferSize` bytes from `*Buffer` using the smallest bin format code that fits. \
Returns `#MsgPack_Error_Success` on success, something else otherwise.
* `MsgPackWriteBin8/16/32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
Same as `MsgPackWriteBinary`, but forces the given format code, even if a smaller one fits.
* `MsgPackReadBinary.q(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)` \
Copies the bin field at the current internal offset into `*OutputBuffer`. \
Returns the number of bytes copied, or `#MsgPack_InvalidSize` on errors. \
The offset isn't moved on errors, so it can be retried with a bigger buffer. \
See: `MsgPackLastError`
* `MsgPackCopyBinary.i(*MsgPackData.MsgPackData)` \
Copies the bin field at the current internal offset into a new buffer that must be freed with `FreeMemory()`. \
Returns `#Null` on errors, or for empty bin fields with `#MsgPack_Error_Success` as the last error. \
See: `MsgPackLastError`
### Strings
Supported encodings: `#PB_UTF8` (default, per spec), `#PB_Ascii`, `#PB_Unicode` (UTF-16 LE).
* `MsgPackWriteString.MsgPack_ErrorCode(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)` \
Writes `String` using the smallest str format code that fits its encoded byte size. \
Returns `#MsgPack_Error_Success` on success, something else otherwise.
* `MsgPackWriteFixStr/Str8/Str16/Str32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)` \
Same as `MsgPackWriteString`, but forces the given format code.
* `MsgPackReadString.s(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)` \
Reads any str field at the current internal offset. \
Returns `""` on errors and sets the internal error code. \
See: `MsgPackLastError`
## Remarks
* Never touch the `MsgPackData` structure unless you **absolutely** have to.
+237 -144
View File
@@ -9,7 +9,7 @@
; * //PureBasic 5.73 LTS (x86/x64)
; * //PureBasic 6.21 (x86/x64)
; * //PureBasic 6.21 C Backend (arm64)
;
;
; ==- Links & License -===========================
; License: CC0 1.0 Universal (Public Domain)
; GitHub: https://github.com/aziascreations/PB-MessagePack
@@ -20,6 +20,14 @@
; Sizes are all unsigned 8/16/32, using Q fixes any potential issues !
; The "sized field" helpers below handle every `[FormatCode][Size][Data]` layout
; shared by the str and bin families.
; The `SizeFieldByteLength` parameter is the number of bytes used to store the size:
; * `0` - Size is stored in the format code's lower 5 bits. (FixStr)
; * `1` - Str8, Bin8
; * `2` - Str16, Bin16
; * `4` - Str32, Bin32
; ------------------------------------------------------------------------------
@@ -30,194 +38,279 @@ CompilerIf #PB_Compiler_IsMainFile
CompilerEndIf
XIncludeFile "./_Commons.pbi"
XIncludeFile "./_Utils.pbi"
XIncludeFile "./_Allocators.pbi"
; ------------------------------------------------------------------------------
;- Constants
#MsgPack_Binary_InvalidSize = -1
XIncludeFile "./_Primitives.pbi"
; ------------------------------------------------------------------------------
;- Procedures
;-> Utils
;-> Internal helpers
; Validates `DataSize`, reserves space for the whole field, writes its format code
; and size, and moves the offset right after them.
; The caller is expected to write `DataSize` bytes of data and move the offset afterward.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedFieldHeader(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, DataSize.q)
Protected MaxDataSize.q
Protected ErrorCode.MsgPack_ErrorCode
; TODO: Change this into a simplified global one for strings/maps/arrays/bin !
Procedure.q _MsgPackGetSizeAfterFormatCode(*MsgPackData.MsgPackData)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Binary_InvalidSize
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
; FIXME: Not implemented !
Select SizeFieldByteLength
Case 0 :
MaxDataSize = 31
Case 1 :
MaxDataSize = 255
Case 2 :
MaxDataSize = 65535
Case 4 :
MaxDataSize = 4294967295
Default
DebuggerError("Invalid size field length: " + Str(SizeFieldByteLength))
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
ProcedureReturn #MsgPack_Error_InvalidValue
EndSelect
If DataSize < 0
DebuggerWarning("Received negative size !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
ProcedureReturn #MsgPack_Error_BinaryDataSizeIsNegative
ElseIf DataSize > MaxDataSize
DebuggerWarning("Data is bigger than `" + Str(MaxDataSize) + "` bytes !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
ProcedureReturn #MsgPack_Error_BinaryDataSizeIsTooBig
EndIf
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
Select SizeFieldByteLength
Case 0
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode | DataSize)
Case 1
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, DataSize)
Case 2
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU16(DataSize))
Case 4
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(DataSize))
EndSelect
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + SizeFieldByteLength
ProcedureReturn #MsgPack_Error_Success
EndProcedure
; Writes a full `[FormatCode][Size][Data]` field by copying `DataSize` bytes from `*Data`.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, *Data, DataSize.q)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, DataSize)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
If DataSize > 0
CopyMemory(*Data, *MsgPackData\Buffer + *MsgPackData\BufferOffset, DataSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + DataSize
EndIf
ProcedureReturn #MsgPack_Error_Success
EndProcedure
; Reads the header of a str or bin field at the current offset, without moving it.
; `ExpectedDataType` must be `#MsgPack_DataTypes_String` or `#MsgPack_DataTypes_Binary`.
; On success, `*HeaderSize` receives the format code + size field length, and `*DataSize`
; the data's length, both guaranteed to be within the buffer.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackData, ExpectedDataType.MsgPack_DataType, *HeaderSize.Integer, *DataSize.Quad)
Protected FormatCode.MsgPack_FormatCode
Protected SizeFieldByteLength.a
Protected DataSize.q
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
If _MsgPackIsAtTheEnd(*MsgPackData)
Debug("End of buffer reached, cannot check data format code !")
*MsgPackData\LastError = #MsgPack_Error_AtOrPastTheEndOfBuffer
ProcedureReturn #MsgPack_Error_AtOrPastTheEndOfBuffer
EndIf
FormatCode = MsgPackGetFormatCode(*MsgPackData)
If MsgPackFormatCodeToDataType(FormatCode) <> ExpectedDataType
Debug("Unexpected format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
ProcedureReturn #MsgPack_Error_InvalidFormatCode
EndIf
Select FormatCode
Case #MsgPack_FormatCode_FixStr:
SizeFieldByteLength = 0
Case #MsgPack_FormatCode_Str8,
#MsgPack_FormatCode_Bin8:
SizeFieldByteLength = 1
Case #MsgPack_FormatCode_Str16,
#MsgPack_FormatCode_Bin16:
SizeFieldByteLength = 2
Case #MsgPack_FormatCode_Str32,
#MsgPack_FormatCode_Bin32:
SizeFieldByteLength = 4
EndSelect
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength)
Debug("End of buffer reached, cannot read the field's size !")
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
ProcedureReturn #MsgPack_Error_BufferTooSmall
EndIf
Select SizeFieldByteLength
Case 0:
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset) & %00011111
Case 1:
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
Case 2:
DataSize = EndianSwapU16(PeekU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
Case 4:
DataSize = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)) & $FFFFFFFF
EndSelect
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
Debug("End of buffer reached, cannot read the field's data !")
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
ProcedureReturn #MsgPack_Error_BufferTooSmall
EndIf
*HeaderSize\i = 1 + SizeFieldByteLength
*DataSize\q = DataSize
*MsgPackData\LastError = #MsgPack_Error_Success
ProcedureReturn #MsgPack_Error_Success
EndProcedure
;-> Getters
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
; Copies the bin field at the current offset into `*OutputBuffer`.
; Returns: The number of bytes copied.
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
; The offset is left untouched, so a bigger buffer can be given afterward.
Procedure.q MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
Protected HeaderSize.i
Protected DataSize.q
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
ProcedureReturn #MsgPack_InvalidSize
EndIf
If DataSize > OutputBufferSize
Debug("Output buffer is too small ! (" + Str(OutputBufferSize) + " < " + Str(DataSize) + ")")
*MsgPackData\LastError = #MsgPack_Error_OutputBufferTooSmall
ProcedureReturn #MsgPack_InvalidSize
EndIf
If DataSize > 0
CompilerIf Not #MsgPack_DisableNullChecks
If Not *OutputBuffer
DebuggerError("A #Null output buffer was passed !")
*MsgPackData\LastError = #MsgPack_Error_NullPointerGiven
ProcedureReturn #MsgPack_InvalidSize
EndIf
CompilerEndIf
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *OutputBuffer, DataSize)
EndIf
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
ProcedureReturn DataSize
EndProcedure
Procedure.i MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
; Copies the bin field at the current offset into a new buffer.
; Returns: A buffer from `AllocateMemory()` that the caller must free with `FreeMemory()`.
; Its size can be obtained with `MemorySize()`.
; Empty bin fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
Protected HeaderSize.i
Protected DataSize.q
Protected *ReturnedValue = #Null
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
ProcedureReturn #Null
EndIf
If DataSize > 0
*ReturnedValue = AllocateMemory(DataSize, #PB_Memory_NoClear)
If Not *ReturnedValue
Debug("Failed to allocate memory for the copied binary !")
*MsgPackData\LastError = #MsgPack_Error_FailedToAllocateBuffer
ProcedureReturn #Null
EndIf
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *ReturnedValue, DataSize)
EndIf
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
ProcedureReturn *ReturnedValue
EndProcedure
;-> Setters
; Writes `BufferSize` bytes from `*Buffer` as a bin8.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteBin8(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
*MsgPackData\LastError = #MsgPack_Error_Success
If BufferSize < 0
DebuggerWarning("Received negative size !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
ProcedureReturn *MsgPackData\LastError
ElseIf BufferSize > 255
DebuggerWarning("Binary buffer is bigger than `(2^8)-1` bytes !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
ProcedureReturn *MsgPackData\LastError
EndIf
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 2)
Protected GrowthErrorCode.MsgPack_ErrorCode
Debug("Growing buffer by `" + Str(BufferSize + 2) + "` bytes for bin8...")
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 2)
If GrowthErrorCode <> #MsgPack_Error_Success
; The structure's error code field is set by the called function !
ProcedureReturn GrowthErrorCode
EndIf
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin8)
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, BufferSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin8, 1, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` as a bin16, even if a smaller format code fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteBin16(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
*MsgPackData\LastError = #MsgPack_Error_Success
If BufferSize < 0
DebuggerWarning("Received negative size !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
ProcedureReturn *MsgPackData\LastError
ElseIf BufferSize > 65535
DebuggerWarning("Binary buffer is bigger than `(2^16)-1` bytes !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
ProcedureReturn *MsgPackData\LastError
EndIf
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 3)
Protected GrowthErrorCode.MsgPack_ErrorCode
Debug("Growing buffer by `" + Str(BufferSize + 3) + "` bytes for bin16...")
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 3)
If GrowthErrorCode <> #MsgPack_Error_Success
; The structure's error code field is set by the called function !
ProcedureReturn GrowthErrorCode
EndIf
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin16)
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU(BufferSize))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin16, 2, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` as a bin32, even if a smaller format code fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteBin32(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
*MsgPackData\LastError = #MsgPack_Error_Success
If BufferSize < 0
DebuggerWarning("Received negative size !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
ProcedureReturn *MsgPackData\LastError
ElseIf BufferSize > 4294967295
DebuggerWarning("Binary buffer is bigger than `(2^32)-1` bytes !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
ProcedureReturn *MsgPackData\LastError
EndIf
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 5)
Protected GrowthErrorCode.MsgPack_ErrorCode
Debug("Growing buffer by `" + Str(BufferSize + 5) + "` bytes for bin16...")
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 5)
If GrowthErrorCode <> #MsgPack_Error_Success
; The structure's error code field is set by the called function !
ProcedureReturn GrowthErrorCode
EndIf
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin32)
PokeI(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI(BufferSize))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin32, 4, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` using the smallest bin format code that fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteBinary(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
If BufferSize < 0
DebuggerWarning("Received negative size !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
ProcedureReturn *MsgPackData\LastError
ElseIf BufferSize <= 255
If BufferSize <= 255
ProcedureReturn MsgPackWriteBin8(*MsgPackData, *Buffer, BufferSize)
ElseIf BufferSize <= 65535
ElseIf BufferSize <= 65535
ProcedureReturn MsgPackWriteBin16(*MsgPackData, *Buffer, BufferSize)
ElseIf BufferSize <= 4294967295
Else
; Sizes above `(2^32)-1` are rejected by the helper.
ProcedureReturn MsgPackWriteBin32(*MsgPackData, *Buffer, BufferSize)
Else
DebuggerWarning("Binary buffer is bigger than `(2^32)-1` bytes !")
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
ProcedureReturn *MsgPackData\LastError
EndIf
EndIf
EndProcedure
+6
View File
@@ -146,10 +146,16 @@ Enumeration _MsgPack_ErrorCodes
#MsgPack_Error_AtOrPastTheEndOfBuffer = 11
#MsgPack_Error_ReadPastEndOfBuffer = 12
#MsgPack_Error_InvalidFormatCode = 13
#MsgPack_Error_FailedToAllocateBuffer = 14
; 5x - Binary
#MsgPack_Error_BinaryDataSizeIsNegative = 50
#MsgPack_Error_BinaryDataSizeIsTooBig = 51
#MsgPack_Error_OutputBufferTooSmall = 52
; 6x - Strings
#MsgPack_Error_UnsupportedStringEncoding = 60
#MsgPack_Error_InvalidStringSize = 61
#MsgPack_Error_CannotGrowBufferDueToConfig = 100
+33
View File
@@ -0,0 +1,33 @@
;{- Code Header
; ==- Basic Info -================================
; Name: _Extensions.pbi
; Version: 0.0.0
; Author: Herwin Bozet (NibblePoker)
;
; ==- Compatibility -=============================
; Tested compiler version:
; * //PureBasic 5.73 LTS (x86/x64)
; * //PureBasic 6.21 (x86/x64)
; * //PureBasic 6.21 C Backend (arm64)
;
; ==- Links & License -===========================
; License: CC0 1.0 Universal (Public Domain)
; GitHub: https://github.com/aziascreations/PB-MessagePack
;}
; ------------------------------------------------------------------------------
;- Notes
; Specification:
; * https://github.com/msgpack/msgpack/blob/master/spec.md
; ------------------------------------------------------------------------------
;- Compiler directive
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
XIncludeFile "./_Commons.pbi"
+1
View File
@@ -37,6 +37,7 @@ XIncludeFile "./_Allocators.pbi"
; Ensures `Size` bytes can be written at the current offset, growing the buffer by
; exactly the missing amount if needed.
; Sets the structure's error code and returns it.
; FIXME: Move elsewhere since it's used in multiple places !
Procedure.MsgPack_ErrorCode _MsgPackReserveWrite(*MsgPackData.MsgPackData, Size.i)
Protected MissingSize.i
+169 -2
View File
@@ -9,24 +9,191 @@
; * //PureBasic 5.73 LTS (x86/x64)
; * //PureBasic 6.21 (x86/x64)
; * //PureBasic 6.21 C Backend (arm64)
;
;
; ==- Links & License -===========================
; License: CC0 1.0 Universal (Public Domain)
; GitHub: https://github.com/aziascreations/PB-MessagePack
;}
; ------------------------------------------------------------------------------
;- Notes
; The spec defines str as UTF-8, which is the default everywhere.
; `#PB_Ascii` and `#PB_Unicode` (UTF-16 LE) are supported for peers that don't follow it.
; Sizes are always in bytes of the encoded string, without any null terminator.
; Strings containing a null character will be truncated on read, as with `PeekS()`.
; ------------------------------------------------------------------------------
;- Compiler directive
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
XIncludeFile "./_Commons.pbi"
XIncludeFile "./_Allocators.pbi"
XIncludeFile "./_Binary.pbi"
; ------------------------------------------------------------------------------
;- Private macros
Macro _MsgPackIsSupportedStringEncoding(Encoding)
(Encoding = #PB_UTF8 Or Encoding = #PB_Ascii Or Encoding = #PB_Unicode)
EndMacro
; ------------------------------------------------------------------------------
;- Procedures
;-> Getters & Setters
;-> Internal helpers
; Returns the byte length of `String` once encoded in `Encoding`, without a null terminator.
; Sets the structure's error code and returns `#MsgPack_InvalidSize` on unsupported encodings.
Procedure.q _MsgPackGetEncodedStringSize(*MsgPackData.MsgPackData, String.s, Encoding.i)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_InvalidSize
EndIf
CompilerEndIf
If Not _MsgPackIsSupportedStringEncoding(Encoding)
DebuggerError("Unsupported string encoding: " + Str(Encoding))
*MsgPackData\LastError = #MsgPack_Error_UnsupportedStringEncoding
ProcedureReturn #MsgPack_InvalidSize
EndIf
ProcedureReturn StringByteLength(String, Encoding)
EndProcedure
; Writes a full str field whose encoded size is already known.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteStringField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, String.s, StringSize.q, Encoding.i)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, StringSize)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
If StringSize > 0
PokeS(*MsgPackData\Buffer + *MsgPackData\BufferOffset, String, -1, Encoding | #PB_String_NoZero)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + StringSize
EndIf
ProcedureReturn #MsgPack_Error_Success
EndProcedure
; Writes a str field with an explicit format code.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteStringAs(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, String.s, Encoding.i)
Protected StringSize.q
StringSize = _MsgPackGetEncodedStringSize(*MsgPackData, String, Encoding)
If StringSize = #MsgPack_InvalidSize
ProcedureReturn MsgPackLastError(*MsgPackData)
EndIf
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, FormatCode, SizeFieldByteLength, String, StringSize, Encoding)
EndProcedure
;-> Getters
; Reads the str field at the current offset and decodes it from `Encoding`.
; Returns: The decoded string.
; OnError: Returns `""` and sets the `MsgPackData\LastError` field.
Procedure.s MsgPackReadString(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)
Protected HeaderSize.i
Protected StringSize.q
Protected *StringStart
Protected ReturnedValue.s = ""
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_String, @HeaderSize, @StringSize) <> #MsgPack_Error_Success
ProcedureReturn ""
EndIf
If Not _MsgPackIsSupportedStringEncoding(Encoding)
DebuggerError("Unsupported string encoding: " + Str(Encoding))
*MsgPackData\LastError = #MsgPack_Error_UnsupportedStringEncoding
ProcedureReturn ""
EndIf
If Encoding = #PB_Unicode And StringSize % 2 <> 0
DebuggerWarning("UTF-16 string has an odd byte size !")
*MsgPackData\LastError = #MsgPack_Error_InvalidStringSize
ProcedureReturn ""
EndIf
*StringStart = *MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize
If StringSize > 0
Select Encoding
Case #PB_UTF8
ReturnedValue = PeekS(*StringStart, StringSize, #PB_UTF8 | #PB_ByteLength)
Case #PB_Ascii
ReturnedValue = PeekS(*StringStart, StringSize, #PB_Ascii)
Case #PB_Unicode
ReturnedValue = PeekS(*StringStart, StringSize / 2, #PB_Unicode)
EndSelect
EndIf
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + StringSize
ProcedureReturn ReturnedValue
EndProcedure
;-> Setters
; Writes `String` encoded as `Encoding` using the smallest str format code that fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteString(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)
Protected StringSize.q
StringSize = _MsgPackGetEncodedStringSize(*MsgPackData, String, Encoding)
If StringSize = #MsgPack_InvalidSize
ProcedureReturn MsgPackLastError(*MsgPackData)
EndIf
If StringSize <= 31
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_FixStr, 0, String, StringSize, Encoding)
ElseIf StringSize <= 255
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str8, 1, String, StringSize, Encoding)
ElseIf StringSize <= 65535
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str16, 2, String, StringSize, Encoding)
Else
; Sizes above `(2^32)-1` are rejected by the helper.
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str32, 4, String, StringSize, Encoding)
EndIf
EndProcedure
; ------------------------------------------------------------------------------
;- Macros
Macro MsgPackWriteFixStr(MsgPackData, String, Encoding = #PB_UTF8)
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_FixStr, 0, String, Encoding)
EndMacro
Macro MsgPackWriteStr8(MsgPackData, String, Encoding = #PB_UTF8)
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str8, 1, String, Encoding)
EndMacro
Macro MsgPackWriteStr16(MsgPackData, String, Encoding = #PB_UTF8)
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str16, 2, String, Encoding)
EndMacro
Macro MsgPackWriteStr32(MsgPackData, String, Encoding = #PB_UTF8)
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str32, 4, String, Encoding)
EndMacro