1 /**
2     Serialization Interface
3 
4     Interfaces for creating classes which may be serialized and deserialized.
5 
6     Copyright:
7         Copyright © 2023-2025, Kitsunebi Games
8         Copyright © 2023-2025, Inochi2D Project
9     
10     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11     Authors:   Luna Nielsen
12 */
13 module nulib.io.serialize;
14 import nulib.string;
15 import numem;
16 
17 /**
18     Interface a class may implement to indicate it can be serialized.
19 */
20 interface ISerializable {
21 @nogc:
22 
23     /**
24         Called on the object to serialize it using the 
25         given (de)serializer.
26 
27         Params:
28             serde = the (de)serializer to use.
29     */
30     void serialize(ref Serde serde);
31 }
32 
33 /**
34     Gets whether the type can be serialized using an
35     elaborate serializer function.
36 */
37 enum hasElaborateSerializer(T) =
38     is(T : ISerializable) ||
39     is(typeof((ref Serde serde) { auto t = T.init; t.serialize(serde); }));
40 
41 /**
42     Interface a class may implement to indicate it can be deserialized.
43 */
44 interface IDeserializable {
45 @nogc:
46 
47     /**
48         Called on the object to deserialize it using the 
49         given (de)serializer.
50 
51         Params:
52             serde = the (de)serializer to use.
53     */
54     void deserialize(ref Serde serde);
55 }
56 
57 /**
58     Gets whether the type can be deserialized using an
59     elaborate serializer function.
60 */
61 enum hasElaborateDeserializer(T) =
62     is(T : IDeserializable) ||
63     is(typeof((ref Serde serde) { auto t = T.init; t.deserialize(serde); }));
64 
65 /**
66     A (de)serializer.
67 */
68 abstract
69 class Serde : NuObject {
70 private:
71 @nogc:
72     SerdeContext ctx;
73 
74 protected:
75 
76     /**
77         Called when the serialization context is initialized.
78     */
79     abstract SerdeContext onInit();
80 
81     /**
82         A serialization function which serializes a string.
83 
84         Params:
85             value = The value to serialize.
86     */
87     abstract void serializeString(string value);
88 
89     /**
90         A serialization function which serializes a signed integer.
91         (max 64-bit)
92 
93         Params:
94             value = The value to serialize, sign extended.
95             bytes = The actual amount of bytes to serialize.
96     */
97     abstract void serializeInt(long value, ubyte bytes);
98 
99     /**
100         A serialization function which serializes an unsigned
101         integer. (max 64-bit)
102 
103         Params:
104             value = The value to serialize, sign extended.
105             bytes = The actual amount of bytes to serialize.
106     */
107     abstract void serializeUInt(ulong value, ubyte bytes);
108 
109     /**
110         A serialization function which serializes a floating
111         point number. (max 64-bit)
112 
113         Params:
114             value = The value to serialize.
115             bytes = The actual amount of bytes to serialize.
116     */
117     abstract void serializeFloat(double value, ubyte bytes);
118 
119     /**
120         A deserialization function which deserializes a string.
121 
122         Returns:
123             The deserialized value
124     */
125     abstract nstring deserializeString();
126 
127     /**
128         A serialization function which serializes a signed integer.
129         (max 64-bit)
130 
131         Params:
132             bytes = The amount of bytes to deserialize.
133 
134         Returns:
135             The deserialized value
136     */
137     abstract long deserializeInt(ubyte bytes);
138 
139     /**
140         A serialization function which serializes an unsigned
141         integer. (max 64-bit)
142 
143         Params:
144             bytes = The amount of bytes to deserialize.
145 
146         Returns:
147             The deserialized value
148     */
149     abstract ulong deserializeUInt(ubyte bytes);
150 
151     /**
152         A serialization function which serializes a floating
153         point number. (max 64-bit)
154 
155         Params:
156             bytes = The amount of bytes to deserialize.
157 
158         Returns:
159             The deserialized value
160     */
161     abstract double deserializeFloat(ubyte bytes);
162 
163     /**
164         Begins a complex serialization context.
165     */
166     abstract SerdeContext beginComplexContext();
167 
168     /**
169         Ends a complex serde context.
170     */
171     abstract void endComplexContext(SerdeContext ctx);
172 
173 public:
174 
175     // Destructor
176     ~this() { nogc_delete(ctx); }
177 
178     /**
179         Constructs a Serde object.
180     */
181     this() {
182         ctx = this.onInit();
183     }
184 
185     /**
186         The name of the format that the (de)serializer 
187         supports.
188     */
189     abstract @property string format();
190 
191     /**
192         The public serialization interface.
193     */
194     void serialize(T)(auto ref T item) {
195         static if (is(T : string)) {
196             serializeString(item);
197         } else static if (__traits(isFloating, T)) {
198             serializeFloat(cast(double)item, T.sizeof);
199         } else static if (__traits(isIntegral, T) && __traits(isUnsigned, T)) {
200             serializeUInt(cast(ulong)item, T.sizeof);
201         } else static if (__traits(isIntegral, T) && !__traits(isUnsigned, T)) {
202             serializeInt(cast(ulong)item, T.sizeof);
203         } else static if (hasElaborateSerializer!T) {
204             Serde self = this;
205             item.serialize(self);
206         } static assert(0, "Can't serialize given type "~T.stringof~".");
207     }
208     
209     /**
210         The public deserialization interface.
211     */
212     void deserialize(T)(auto ref T item) { 
213         static if (is(T : string)) {
214             serializeString(item);
215         } else static if (__traits(isFloating, T)) {
216             serializeFloat(cast(double)item, T.sizeof);
217         } else static if (__traits(isIntegral, T) && __traits(isUnsigned, T)) {
218             serializeUInt(cast(ulong)item, T.sizeof);
219         } else static if (__traits(isIntegral, T) && !__traits(isUnsigned, T)) {
220             serializeInt(cast(ulong)item, T.sizeof);
221         } else static if (hasElaborateDeserializer!T) {
222             Serde self = this;
223             item.deserialize(self);
224         } static assert(0, "Can't deserialize given type "~T.stringof~".");
225     }
226 }
227 
228 /**
229     A context used during (de)serialization to traverse complex
230     nested serialization trees.
231 */
232 abstract
233 class SerdeContext : NuObject  {
234 private:
235     Serde owner_;
236 
237 public:
238 @nogc:
239 
240     /**
241         Constructor for a context.
242     */
243     this(Serde owner) { this.owner_ = owner; }
244 
245     /**
246         The owner object which created the context.
247     */
248     final
249     @property Serde owner() { return owner_; }
250 
251     /**
252         Begins an object within the context.
253     */
254     abstract SerdeContext beginObject();
255 
256     /**
257         Ends an object within the context.
258     */
259     abstract void endObject(SerdeContext object);
260 
261     /**
262         Begins a data range within the context.
263     */
264     abstract SerdeContext beginRange();
265 
266     /**
267         Ends a data range within the context.
268     */
269     abstract void endRange(SerdeContext object);
270 }