1 /******************************************************************************
2 3 Provides basic utilities to work with version information in structures.
4 5 Most common idiom used in application code is checking for
6 version support:
7 8 ---
9 static if (Version.Info!(S).exists)
10 {
11 }
12 ---
13 14 It is also possible to manually figure out whole version increment
15 chain based on provided metadata (though use cases for that
16 are supposed to be rare)
17 18 Copyright:
19 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
20 All rights reserved.
21 22 License:
23 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
24 Alternatively, this file may be distributed under the terms of the Tango
25 3-Clause BSD License (see LICENSE_BSD.txt for details).
26 27 *******************************************************************************/28 29 moduleocean.util.serialize.Version;
30 31 32 importocean.meta.types.Qualifiers;
33 importocean.core.Verify;
34 importocean.core.Buffer;
35 36 version (unittest) importocean.core.Test;
37 38 /*******************************************************************************
39 40 Tag type that denotes missing version type for either next or previous
41 version of a struct.
42 43 *******************************************************************************/44 45 publicstructMissingVersion46 {
47 enumexists = false;
48 enumubytenumber = 0;
49 }
50 51 /*******************************************************************************
52 53 Namespace struct is desired because most of symbols have very common and
54 generic names.
55 56 *******************************************************************************/57 58 structVersion59 {
60 /***************************************************************************
61 62 The type of the version number tag that is prepended to serialised data.
63 64 ***************************************************************************/65 66 publicaliasubyteType;
67 68 /***************************************************************************
69 70 Evaluates to version information of S if S is a versioned struct:
71 72 - exists: true if S is a versioned struct or false if S is a struct
73 without version or not a struct. If false the other constants are
74 not defined.
75 - number: The value of the struct version (S.Info), expected to
76 be of type Type.
77 - next/prev: Info for S.StructNext or S.StructPrev, respectively
78 - type : type of struct this info belongs to
79 80 next/prev are recursive instances of this template so they can be
81 checked for existence by Info!(S).next.exists
82 If that's true the next version number is Info!(S).next.number.
83 The next version itself can contain a next version,
84 use Info!(S).next.next.exists to check it out.
85 86 ***************************************************************************/87 88 templateInfo ( S )
89 {
90 staticif (is(S == struct) && is(typeof(S.StructVersion) V))
91 {
92 staticassert (
93 S.StructVersion <= Version.Type.max,
94 S.stringof ~ ".StructVersion == " ~
95 S.StructVersion.stringof ~
96 ", but it must be lower than Version.Type.max"97 );
98 99 enumexists = true;
100 enumnumber = S.StructVersion;
101 102 aliasStype;
103 104 staticif (is(S.StructNext))
105 {
106 aliasInfo!(S.StructNext) next;
107 }
108 else109 {
110 aliasMissingVersionnext; // dummy111 }
112 113 staticif (is(S.StructPrevious))
114 {
115 aliasInfo!(S.StructPrevious) prev;
116 }
117 else118 {
119 aliasMissingVersionprev; // dummy120 }
121 }
122 else123 {
124 aliasMissingVersionInfo;
125 }
126 }
127 128 unittest129 {
130 structS { }
131 staticassert (is(Info!(S) == MissingVersion));
132 staticassert (! Info!(S).exists);
133 }
134 135 unittest136 {
137 structS1 { enumStructVersion = 1; }
138 aliasInfo!(S1) Ver;
139 staticassert (Ver.exists);
140 staticassert (Ver.number == 1);
141 staticassert (is(Ver.next == MissingVersion));
142 staticassert (is(Ver.prev == MissingVersion));
143 144 structS2 { enumStructVersion = Version.Type.max + 1; }
145 staticassert (!is(typeof(Info!(S2))));
146 }
147 148 unittest149 {
150 structS151 {
152 enumStructVersion = 1;
153 aliasSStructPrevious;
154 aliasSStructNext;
155 }
156 157 aliasInfo!(S) Ver;
158 159 staticassert (Ver.exists);
160 staticassert (is(Ver.next.type == S));
161 staticassert (is(Ver.prev.type == S));
162 }
163 164 /***************************************************************************
165 166 Assumes that input is versioned struct chunk and extracts version number
167 from it. Otherwise will return garbage
168 169 Params:
170 data = serialized struct data, untouched
171 ver = out parameter to store version number
172 173 Returns:
174 data slices after the version bytes
175 176 ***************************************************************************/177 178 staticvoid[] extract ( void[] data, refVersion.Typever )
179 {
180 verify (data.length > Version.Type.sizeof);
181 182 ver = *(cast(Version.Type*) data.ptr);
183 returndata[Version.Type.sizeof .. $];
184 }
185 186 unittest187 {
188 Version.TypeV = 42;
189 void[] data = [ V, cast(Version.Type) 1, cast(Version.Type) 1 ];
190 Version.Typever;
191 autodata_unver = extract(data, ver);
192 test!("==")(ver, V);
193 test!("==")(data_unver.length, 2);
194 }
195 196 staticconst(void)[] extract ( invoid[] data, refVersion.Typever )
197 {
198 returnextract(cast(void[]) data, ver);
199 }
200 201 unittest202 {
203 Version.TypeV = 42;
204 void[] data = [ V, cast(Version.Type) 1, cast(Version.Type) 1 ];
205 const(void[]) cdata = data;
206 Version.Typever;
207 const(void)[] data_unver = extract(cdata, ver);
208 test!("==")(ver, V);
209 test!("==")(data_unver.length, 2);
210 }
211 212 213 /***************************************************************************
214 215 Writes version data in the beginning of provided data buffer. Grows buffer
216 if it is too small. Call this function before actually writing any useful
217 payload to the buffer or it will be overwritten.
218 219 Params:
220 data = any byte buffer, will be modified to start with version info
221 ver = version number to inject
222 223 Returns:
224 slice of data after the version data. Use that slice to add actual
225 payload
226 227 ***************************************************************************/228 229 staticvoid[] inject ( refBuffer!(void) data, Version.Typever )
230 {
231 if (data.length < Version.Type.sizeof)
232 {
233 data.length = Version.Type.sizeof;
234 }
235 236 *(cast(Version.Type*) data[].ptr) = ver;
237 238 returndata[Version.Type.sizeof .. data.length];
239 }
240 241 /// ditto242 staticvoid[] inject ( refvoid[] data, Version.Typever )
243 {
244 returninject(*cast(Buffer!(void)*) &data, ver);
245 }
246 247 unittest248 {
249 Version.TypeV = 42;
250 void[] data = [ cast(ubyte) 1, 2, 3 ];
251 autoresult = inject(data, V);
252 test!("is")(data.ptr + V.sizeof, result.ptr);
253 test!("==")(data, [ V, 2, 3 ][]);
254 }
255 }