… | |
… | |
15 | |
15 | |
16 | =cut |
16 | =cut |
17 | |
17 | |
18 | package Types::Serialiser; |
18 | package Types::Serialiser; |
19 | |
19 | |
20 | use common::sense; |
20 | use common::sense; # required to suppress annoying warnings |
21 | |
21 | |
22 | our $VERSION = 0.01; |
22 | our $VERSION = '1.01'; |
23 | |
23 | |
24 | =head1 SIMPLE SCALAR CONSTANTS |
24 | =head1 SIMPLE SCALAR CONSTANTS |
25 | |
25 | |
26 | Simple scalar constants are values that are overloaded to act like simple |
26 | Simple scalar constants are values that are overloaded to act like simple |
27 | Perl values, but have (class) type to differentiate them from normal Perl |
27 | Perl values, but have (class) type to differentiate them from normal Perl |
28 | scalars. This is necessary because these have different representations in |
28 | scalars. This is necessary because these have different representations in |
29 | the serialisation formats. |
29 | the serialisation formats. |
30 | |
30 | |
|
|
31 | In the following, functions with zero or one arguments have a prototype of |
|
|
32 | C<()> and C<($)>, respectively, so act as constants and unary operators. |
|
|
33 | |
31 | =head2 BOOLEANS (Types::Serialiser::Boolean class) |
34 | =head2 BOOLEANS (Types::Serialiser::Boolean class) |
32 | |
35 | |
33 | This type has only two instances, true and false. A natural representation |
36 | This type has only two instances, true and false. A natural representation |
34 | for these in Perl is C<1> and C<0>, but serialisation formats need to be |
37 | for these in Perl is C<1> and C<0>, but serialisation formats need to be |
35 | able to differentiate between them and mere numbers. |
38 | able to differentiate between them and mere numbers. |
… | |
… | |
51 | the number C<0>. It is up to you whether you use the variable form |
54 | the number C<0>. It is up to you whether you use the variable form |
52 | (C<$Types::Serialiser::false>) or the constant form (C<Types::Serialiser::false>). |
55 | (C<$Types::Serialiser::false>) or the constant form (C<Types::Serialiser::false>). |
53 | |
56 | |
54 | The constant is represented as a reference to a scalar containing C<0> - |
57 | The constant is represented as a reference to a scalar containing C<0> - |
55 | implementations are allowed to directly test for this. |
58 | implementations are allowed to directly test for this. |
|
|
59 | |
|
|
60 | =item Types::Serialiser::as_bool $value |
|
|
61 | |
|
|
62 | Converts a Perl scalar into a boolean, which is useful syntactic |
|
|
63 | sugar. Strictly equivalent to: |
|
|
64 | |
|
|
65 | $value ? $Types::Serialiser::true : $Types::Serialiser::false |
56 | |
66 | |
57 | =item $is_bool = Types::Serialiser::is_bool $value |
67 | =item $is_bool = Types::Serialiser::is_bool $value |
58 | |
68 | |
59 | Returns true iff the C<$value> is either C<$Types::Serialiser::true> or |
69 | Returns true iff the C<$value> is either C<$Types::Serialiser::true> or |
60 | C<$Types::Serialiser::false>. |
70 | C<$Types::Serialiser::false>. |
… | |
… | |
97 | |
107 | |
98 | =back |
108 | =back |
99 | |
109 | |
100 | =cut |
110 | =cut |
101 | |
111 | |
|
|
112 | BEGIN { |
|
|
113 | # for historical reasons, and to avoid extra dependencies in JSON::PP, |
|
|
114 | # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. |
|
|
115 | package JSON::PP::Boolean; |
|
|
116 | |
|
|
117 | *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; |
|
|
118 | } |
|
|
119 | |
|
|
120 | { |
|
|
121 | # this must done before blessing to work around bugs |
|
|
122 | # in perl < 5.18 (it seems to be fixed in 5.18). |
|
|
123 | package Types::Serialiser::BooleanBase; |
|
|
124 | |
|
|
125 | use overload |
|
|
126 | "0+" => sub { ${$_[0]} }, |
|
|
127 | "++" => sub { $_[0] = ${$_[0]} + 1 }, |
|
|
128 | "--" => sub { $_[0] = ${$_[0]} - 1 }, |
|
|
129 | fallback => 1; |
|
|
130 | |
|
|
131 | @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; |
|
|
132 | } |
|
|
133 | |
102 | our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; |
134 | our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; |
103 | our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; |
135 | our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; |
104 | our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; |
136 | our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; |
105 | |
137 | |
106 | sub true () { $true } |
138 | sub true () { $true } |
107 | sub false () { $false } |
139 | sub false () { $false } |
108 | sub error () { $error } |
140 | sub error () { $error } |
|
|
141 | |
|
|
142 | sub as_bool($) { $_[0] ? $true : $false } |
109 | |
143 | |
110 | sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
144 | sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
111 | sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
145 | sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
112 | sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
146 | sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
113 | sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } |
147 | sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } |
114 | |
148 | |
115 | package Types::Serialiser::Boolean; |
|
|
116 | |
|
|
117 | use overload |
|
|
118 | "0+" => sub { ${$_[0]} }, |
|
|
119 | "++" => sub { $_[0] = ${$_[0]} + 1 }, |
|
|
120 | "--" => sub { $_[0] = ${$_[0]} - 1 }, |
|
|
121 | fallback => 1; |
|
|
122 | |
|
|
123 | package Types::Serialiser::Error; |
149 | package Types::Serialiser::Error; |
124 | |
150 | |
125 | sub error { |
151 | sub error { |
126 | require Carp; |
152 | require Carp; |
127 | Carp::croak ("caught attempt to use Types::Serialiser::error value"); |
153 | Carp::croak ("caught attempt to use the Types::Serialiser::error value"); |
128 | }; |
154 | }; |
129 | |
155 | |
130 | use overload |
156 | use overload |
131 | "0+" => \&error, |
157 | "0+" => \&error, |
132 | "++" => \&error, |
158 | "++" => \&error, |
133 | "--" => \&error, |
159 | "--" => \&error, |
134 | fallback => 1; |
160 | fallback => 1; |
135 | |
161 | |
|
|
162 | =head1 NOTES FOR XS USERS |
|
|
163 | |
|
|
164 | The recommended way to detect whether a scalar is one of these objects |
|
|
165 | is to check whether the stash is the C<Types::Serialiser::Boolean> or |
|
|
166 | C<Types::Serialiser::Error> stash, and then follow the scalar reference to |
|
|
167 | see if it's C<1> (true), C<0> (false) or C<undef> (error). |
|
|
168 | |
|
|
169 | While it is possible to use an isa test, directly comparing stash pointers |
|
|
170 | is faster and guaranteed to work. |
|
|
171 | |
|
|
172 | For historical reasons, the C<Types::Serialiser::Boolean> stash is |
|
|
173 | just an alias for C<JSON::PP::Boolean>. When printed, the classname |
|
|
174 | with usually be C<JSON::PP::Boolean>, but isa tests and stash pointer |
|
|
175 | comparison will normally work correctly (i.e. Types::Serialiser::true ISA |
|
|
176 | JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). |
|
|
177 | |
|
|
178 | =head1 A GENERIC OBJECT SERIALIATION PROTOCOL |
|
|
179 | |
|
|
180 | This section explains the object serialisation protocol used by |
|
|
181 | L<CBOR::XS>. It is meant to be generic enough to support any kind of |
|
|
182 | generic object serialiser. |
|
|
183 | |
|
|
184 | This protocol is called "the Types::Serialiser object serialisation |
|
|
185 | protocol". |
|
|
186 | |
|
|
187 | =head2 ENCODING |
|
|
188 | |
|
|
189 | When the encoder encounters an object that it cannot otherwise encode (for |
|
|
190 | example, L<CBOR::XS> can encode a few special types itself, and will first |
|
|
191 | attempt to use the special C<TO_CBOR> serialisation protocol), it will |
|
|
192 | look up the C<FREEZE> method on the object. |
|
|
193 | |
|
|
194 | Note that the C<FREEZE> method will normally be called I<during> encoding, |
|
|
195 | and I<MUST NOT> change the data structure that is being encoded in any |
|
|
196 | way, or it might cause memory corruption or worse. |
|
|
197 | |
|
|
198 | If it exists, it will call it with two arguments: the object to serialise, |
|
|
199 | and a constant string that indicates the name of the data model. For |
|
|
200 | example L<CBOR::XS> uses C<CBOR>, and the L<JSON> and L<JSON::XS> modules |
|
|
201 | (or any other JSON serialiser), would use C<JSON> as second argument. |
|
|
202 | |
|
|
203 | The C<FREEZE> method can then return zero or more values to identify the |
|
|
204 | object instance. The serialiser is then supposed to encode the class name |
|
|
205 | and all of these return values (which must be encodable in the format) |
|
|
206 | using the relevant form for Perl objects. In CBOR for example, there is a |
|
|
207 | registered tag number for encoded perl objects. |
|
|
208 | |
|
|
209 | The values that C<FREEZE> returns must be serialisable with the serialiser |
|
|
210 | that calls it. Therefore, it is recommended to use simple types such as |
|
|
211 | strings and numbers, and maybe array references and hashes (basically, the |
|
|
212 | JSON data model). You can always use a more complex format for a specific |
|
|
213 | data model by checking the second argument, the data model. |
|
|
214 | |
|
|
215 | The "data model" is not the same as the "data format" - the data model |
|
|
216 | indicates what types and kinds of return values can be returned from |
|
|
217 | C<FREEZE>. For example, in C<CBOR> it is permissible to return tagged CBOR |
|
|
218 | values, while JSON does not support these at all, so C<JSON> would be a |
|
|
219 | valid (but too limited) data model name for C<CBOR::XS>. similarly, a |
|
|
220 | serialising format that supports more or less the same data model as JSON |
|
|
221 | could use C<JSON> as data model without losing anything. |
|
|
222 | |
|
|
223 | =head2 DECODING |
|
|
224 | |
|
|
225 | When the decoder then encounters such an encoded perl object, it should |
|
|
226 | look up the C<THAW> method on the stored classname, and invoke it with the |
|
|
227 | classname, the constant string to identify the data model/data format, and |
|
|
228 | all the return values returned by C<FREEZE>. |
|
|
229 | |
|
|
230 | =head2 EXAMPLES |
|
|
231 | |
|
|
232 | See the C<OBJECT SERIALISATION> section in the L<CBOR::XS> manpage for |
|
|
233 | more details, an example implementation, and code examples. |
|
|
234 | |
|
|
235 | Here is an example C<FREEZE>/C<THAW> method pair: |
|
|
236 | |
|
|
237 | sub My::Object::FREEZE { |
|
|
238 | my ($self, $model) = @_; |
|
|
239 | |
|
|
240 | ($self->{type}, $self->{id}, $self->{variant}) |
|
|
241 | } |
|
|
242 | |
|
|
243 | sub My::Object::THAW { |
|
|
244 | my ($class, $model, $type, $id, $variant) = @_; |
|
|
245 | |
|
|
246 | $class->new (type => $type, id => $id, variant => $variant) |
|
|
247 | } |
|
|
248 | |
136 | =head1 BUGS |
249 | =head1 BUGS |
137 | |
250 | |
138 | The use of L<overload> makes this module much heavier than it should be |
251 | The use of L<overload> makes this module much heavier than it should be |
139 | (on my system, this module: 4kB RSS, overload: 260kB RSS). |
252 | (on my system, this module: 4kB RSS, overload: 260kB RSS). |
140 | |
253 | |