Skip to content

Commit 3167158

Browse files
committed
Improve fromString to be usable with user input
fromString now does all the necessary validation, ensuring that user input can be given to it directly.
1 parent 62c2128 commit 3167158

File tree

1 file changed

+53
-16
lines changed

1 file changed

+53
-16
lines changed

source/geod24/bitblob.d

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module geod24.bitblob;
1717

1818
static import std.ascii;
1919
import std.algorithm.iteration : each, map;
20+
import std.algorithm.searching : countUntil, startsWith;
2021
import std.format;
2122
import std.range;
2223
import 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
396432
unittest
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

Comments
 (0)