@@ -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