transforms/Xtreams__MessagePackMarshaler.st
author Martin Kobetic
Sun, 17 Nov 2013 00:23:18 -0500
changeset 147 bd6be28aa924
parent 111 44ac233b2f83
permissions -rw-r--r--
merging

"{ Package: 'stx:goodies/xtreams/transforms' }"

"{ NameSpace: Xtreams }"

Object subclass:#MessagePackMarshaler
	instanceVariableNames:'unmarshaling marshaling analysing'
	classVariableNames:''
	poolDictionaries:'XtreamsPool'
	category:'Xtreams-Transforms'
!

MessagePackMarshaler comment:'MessagePackMarshaler defines the binary format of the MessagePack protocol ( http://wiki.msgpack.org/display/MSGPACK/Format+specification ), which is a binary JSON. It can marshal simple objects, but not whole classes, just like JSON. It is considered to be a very fast marshaler because of its lightweight protocol. It cannot handle circular references.

Instance Variables
	analysing	<Object>	analysing operations
	marshaling	<Dictionary>	marshaling operations
	unmarshaling	<(Array of: (BlockClosure))>	unmarshaling operations



'
!


!MessagePackMarshaler class methodsFor:'instance creation'!

new
	^super new initialize
! !

!MessagePackMarshaler class methodsFor:'constants - containers'!

array16
	^16rDC
!

array32
	^16rDD
!

map16
	^16rDE
!

map32
	^16rDF
!

raw16
	^16rDA
!

raw32
	^16rDB
! !

!MessagePackMarshaler class methodsFor:'constants - numbers'!

double
	^16rCB
!

float
	^16rCA
!

int16
	^16rD1
!

int32
	^16rD2
!

int64
	^16rD3
!

int8
	^16rD0
!

uint16
	^16rCD
!

uint32
	^16rCE
!

uint64
	^16rCF
!

uint8
	^16rCC
! !

!MessagePackMarshaler class methodsFor:'constants - singletons'!

false
	^16rC2
!

nil
	^16rC0
!

true
	^16rC3
! !

!MessagePackMarshaler methodsFor:'api'!

analyse: reading
	| type |
	type := reading uint8 get.
	type <= 16rBF ifTrue: [
		| fixMapOrArray |
		type <= 16r7F ifTrue: [^reading log: 'positive fixnum' do: [type]].
		fixMapOrArray := type bitShift: -4.
		fixMapOrArray = 2r1001 ifTrue: [^reading log: 'fix array' do: [(1 to: (type bitAnd: 2r00001111)) collect: [:i | self analyse: reading]]].
		fixMapOrArray = 2r1000 ifTrue: [
			^reading log: 'fix map' do: [
				| map |
				map := Dictionary new.
				(type bitAnd: 2r00001111) timesRepeat: [
					map at: (self analyse: reading) put: (self analyse: reading)].
				map]].
		^reading log: 'fix raw' do: [reading uint8 read: (type bitAnd: 2r00011111)]].
	(type bitShift: -5) = 2r111 ifTrue: [^reading log: 'negative fixnum' do: [-32 + (type bitAnd: 2r00011111)]].
	^reading log: (type printStringRadix: 16) do: [(analysing at: type) value: reading]
!

marshal: writing object: object
	(marshaling at: object class ifAbsent: [self error: 'Unmarshalable class with the MessagePack protocol']) value: writing value: object
!

unmarshal: reading
	| type |
	type := reading uint8 get.
	type <= 16rBF ifTrue: [
		| fixMapOrArray |
		type <= 16r7F ifTrue: [^type].
		fixMapOrArray := type bitShift: -4.
		fixMapOrArray = 2r1001 ifTrue: [^(1 to: (type bitAnd: 2r00001111)) collect: [:i | self unmarshal: reading]].
		fixMapOrArray = 2r1000 ifTrue: [
			| map |
			map := Dictionary new.
			(type bitAnd: 2r00001111) timesRepeat: [
				map at: (self unmarshal: reading) put: (self unmarshal: reading)].
			^map].
		^reading uint8 read: (type bitAnd: 2r00011111)].
	(type bitShift: -5) = 2r111 ifTrue: [^-32 + (type bitAnd: 2r00011111)].
	^(unmarshaling at: type) value: reading
! !

!MessagePackMarshaler methodsFor:'configuration'!

configureAnalyse: reading
	"This protocol has fixed endianness built in"
	^true
!

configureMarshal: writing
	"This protocol has fixed endianness built in"
	^true
!

configureUnmarshal: reading
	"This protocol has fixed endianness built in"
	^true
! !

!MessagePackMarshaler methodsFor:'initialize-release'!

initialize
	self initializeMarshaling.
	self initializeUnmarshaling.
	self initializeAnalysing.
!

initializeAnalysing
	(analysing := unmarshaling copy)
		at: self class array16 put: [:reading | reading log: 'array16' do: [(1 to: reading uint16 get) collect: [:i | self analyse: reading]]];
		at: self class array32 put: [:reading | reading log: 'array32' do: [(1 to: reading uint32 get) collect: [:i | self analyse: reading]]];
		at: self class map16 put: [:reading |
			reading log: 'map16' do: [
				| map |
				map := Dictionary new.
				reading uint16 get timesRepeat: [
					map at: (self analyse: reading) put: (self analyse:reading)].
				map]];
		at: self class map32 put: [:reading |
			reading log: 'map32' do: [
				| map |
				map := Dictionary new.
				reading uint16 get timesRepeat: [
					map at: (self analyse: reading) put: (self analyse: reading)].
				map]];
		yourself
!

initializeMarshaling
	(marshaling := Dictionary new)
		"singletons"
		at: UndefinedObject put: [:writing :object | writing uint8 put: self class nil];
		at: True put: [:writing :object | writing uint8 put: self class true];
		at: False put: [:writing :object | writing uint8 put: self class false];

		"numbers"
		at: Float put: [:writing :object | writing uint8 put: self class float. writing float put: object];
		at: Double put: [:writing :object | writing uint8 put: self class double. writing double put: object];
		at: SmallDouble put: [:writing :object | writing uint8 put: self class double. writing double put: object];
		yourself.

	Integer allSubclassesDo: [:each |
		marshaling at: each put: [:writing :object | self write: writing integer: object]].

	SequenceableCollection allSubclassesDo: [:each |
		marshaling at: each put: [:writing :object | self write: writing array: object]].
	(KeyedCollection withAllSubclasses, Dictionary withAllSubclasses) do: [:each |
		marshaling at: each put: [:writing :object | self write: writing map: object]].
	(CharacterArray withAllSubclasses, IntegerArray withAllSubclasses) do: [:each |
		marshaling at: each put: [:writing :object | self error: each name, ' are not supported by the MessagePack protocol']].
	marshaling at: ByteArray put: [:writing :object | self write: writing raw: object]
!

initializeUnmarshaling
	(unmarshaling := Array new: 255 withAll: [:reading | self error: 'unknown type'])
		"singletons"
		at: self class nil put: [:reading | nil];
		at: self class true put: [:reading | true];
		at: self class false put: [:reading | false];

		"numbers"
		at: self class uint8 put: [:reading | reading uint8 get];
		at: self class uint16 put: [:reading | reading uint16 get];
		at: self class uint32 put: [:reading | reading uint32 get];
		at: self class uint64 put: [:reading | reading uint64 get];
		at: self class int8 put: [:reading | reading int8 get];
		at: self class int16 put: [:reading | reading int16 get];
		at: self class int32 put: [:reading | reading int32 get];
		at: self class int64 put: [:reading | reading int64 get];
		at: self class float put: [:reading | reading float get];
		at: self class double put: [:reading | reading double get];

		"containers"
		at: self class raw16 put: [:reading | reading uint8 read: reading uint16 get];
		at: self class raw32 put: [:reading | reading uint8 read: reading uint32 get];
		at: self class array16 put: [:reading | (1 to: reading uint16 get) collect: [:i | self unmarshal: reading]];
		at: self class array32 put: [:reading | (1 to: reading uint32 get) collect: [:i | self unmarshal: reading]];
		at: self class map16 put: [:reading |
			| map |
			map := Dictionary new.
			reading uint16 get timesRepeat: [
				map at: (self unmarshal: reading) put: (self unmarshal: reading)].
			map];
		at: self class map32 put: [:reading |
			| map |
			map := Dictionary new.
			reading uint32 get timesRepeat: [
				map at: (self unmarshal: reading) put: (self unmarshal: reading)].
			map];
		yourself
! !

!MessagePackMarshaler methodsFor:'private - writing'!

write: writing array: array
	self write: writing array_size: array size.
	array do: [:each | self marshal: writing object: each]
!

write: writing array_size: size
	size <= 2r00001111 ifTrue: [^writing uint8 put: size + 2r10010000].
	size <= 65535 ifTrue: [
		writing uint8 put: self class array16.
		^writing uint16 put: size].
	size <= 4294967295 ifTrue: [
		writing uint8 put: self class array32.
		^writing uint32 put: size].
	self error: 'array too big to marshal with MessagePack protocol'
!

write: writing integer: integer
	(integer between: 0 and: 127) ifTrue: [^writing uint8 put: integer].
	(integer between: -32 and: -1) ifTrue: [^writing uint8 put: integer + 256].
	integer >= 128 ifTrue: [
		integer <= 255 ifTrue: [^writing uint8 put: self class uint8; put: integer].
		integer <= 65535 ifTrue: [
			writing uint8 put: self class uint16.
			^writing uint16 put: integer].
		integer <= 4294967295 ifTrue: [
			writing uint8 put: self class uint32.
			^writing uint32 put: integer].
		integer <= 18446744073709551615 ifTrue: [
			writing uint8 put: self class uint64.
			^writing uint64 put: integer].
		self error: 'integer too big to marshal with MessagePack protocol'].
	integer >= -128 ifTrue: [
		writing uint8 put: self class int8.
		^writing int8 put: integer].
	integer >= -32768 ifTrue: [
		writing uint8 put: self class int16.
		^writing int16 put: integer].
	integer >= -2147483648 ifTrue: [
		writing uint8 put: self class int32.
		^writing int32 put: integer].
	integer >= -9223372036854775808 ifTrue: [
		writing uint8 put: self class int64.
		^writing int64 put: integer].
	self error: 'integer too small to marshal with MessagePack protocol'.
!

write: writing map: map
	self write: writing map_size: map size.
	map keysAndValuesDo: [:key :value |
		self marshal: writing object: key.
		self marshal: writing object: value]
!

write: writing map_size: size
	size <= 2r00001111 ifTrue: [^writing uint8 put: size + 2r10000000].
	size <= 65535 ifTrue: [
		writing uint8 put: self class map16.
		^writing uint16 put: size].
	size <= 4294967295 ifTrue: [
		writing uint8 put: self class map32.
		^writing uint32 put: size].
	self error: 'array too big to marshal with MessagePack protocol'
!

write: writing raw: bytearray
	self write: writing raw_size: bytearray size.
	writing uint8 write: bytearray
!

write: writing raw_size: size
	size <= 2r00011111 ifTrue: [^writing uint8 put: size + 2r10100000].
	size <= 65535 ifTrue: [
		writing uint8 put: self class raw16.
		^writing uint16 put: size].
	size <= 4294967295 ifTrue: [
		writing uint8 put: self class raw32.
		^writing uint32 put: size].
	self error: 'array too big to marshal with MessagePack protocol'
! !

!MessagePackMarshaler class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '$Id$'
! !