@@ -17,6 +17,7 @@ module geod24.bitblob;
1717
1818static import std.ascii ;
1919import std.algorithm.iteration : each, map;
20+ import std.algorithm.searching : countUntil, startsWith;
2021import std.format ;
2122import std.range ;
2223import std.utf ;
@@ -143,6 +144,41 @@ public struct BitBlob (size_t Size)
143144 return buffer.idup;
144145 }
145146
147+ /* **************************************************************************
148+
149+ Support deserialization
150+
151+ Vibe.d expects the `toString`/`fromString` to be present for it to
152+ correctly serialize and deserialize a type.
153+ This allows to use this type as parameter in `vibe.web.rest` methods,
154+ or use it with Vibe.d's serialization module.
155+ This function does more extensive validation of the input than the
156+ constructor and can be given user input.
157+
158+ ***************************************************************************/
159+
160+ static auto fromString (scope const (char )[] str)
161+ {
162+ // Ignore prefix
163+ if (str.startsWith(" 0x" ) || str.startsWith(" 0X" ))
164+ str = str[2 .. $];
165+
166+ // Then check length
167+ if (str.length != Size * 2 )
168+ throw new Exception (
169+ format(" Cannot parse string '%s' of length %s: Expected %s chars (%s with prefix)" ,
170+ str, str.length, Size * 2 , Size * 2 + 2 ));
171+
172+ // Then content check
173+ auto index = str.countUntil! (e => ! std.ascii.isAlphaNum (e));
174+ if (index != - 1 )
175+ throw new Exception (
176+ format(" String '%s' has non alphanumeric character at index %s" ,
177+ str, index));
178+
179+ return BitBlob (str);
180+ }
181+
146182 pure nothrow @nogc :
147183
148184 /* **************************************************************************
@@ -214,22 +250,6 @@ public struct BitBlob (size_t Size)
214250 this .data[idx++ ] = cast (ubyte )((chunk[0 ] << 4 ) + chunk[1 ]);
215251 }
216252
217- /* **************************************************************************
218-
219- Support deserialization
220-
221- Vibe.d expects the `toString`/`fromString` to be present for it to
222- correctly serialize and deserialize a type.
223- This allows to use this type as parameter in `vibe.web.rest` methods,
224- or use it with Vibe.d's serialization module.
225-
226- ***************************************************************************/
227-
228- static auto fromString (scope const (char )[] str)
229- {
230- return BitBlob! (Size)(str);
231- }
232-
233253 // / Store the internal data
234254 private ubyte [Size] data;
235255
@@ -392,11 +412,28 @@ unittest
392412 assert (collectException! AssertError (Hash(buff)) ! is null );
393413}
394414
415+ // Test that `fromString` throws Exceptions as and when expected
416+ unittest
417+ {
418+ import std.exception ;
419+ alias Hash = BitBlob! (32 );
420+
421+ // Error on the length
422+ assert (collectException! Exception (Hash.fromString(" Hello world" )) ! is null );
423+
424+ char [GenesisBlockHashStr.length] buff = GenesisBlockHashStr;
425+ Hash h = Hash(buff);
426+ buff[5 ] = ' _' ;
427+ // Error on the invalid char
428+ assert (collectException! Exception (Hash.fromString(buff)) ! is null );
429+ }
430+
395431// Make sure the string parsing works at CTFE
396432unittest
397433{
398434 static immutable BitBlob! 32 CTFEability = BitBlob! 32 (GenesisBlockHashStr);
399435 static assert (CTFEability[] == GenesisBlockHash);
436+ static assert (CTFEability == BitBlob! 32. fromString(GenesisBlockHashStr));
400437}
401438
402439// Support for rvalue opCmp
0 commit comments