Skip to content

Commit 050da98

Browse files
committed
sim
1 parent ac1e270 commit 050da98

4 files changed

Lines changed: 201 additions & 92 deletions

File tree

docs/developer-guide/param-and-model-file-structure.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Softmax softmax 1 1 fc prob 0=0
1818
* layer count : count of the layer line follows, should be exactly the count of all layer names
1919
* blob count : count of all blobs, usually greater than or equals to the layer count
2020
### layer line
21+
22+
each layer must occupy exactly one physical line, including all blob names and parameters; do not split a layer across lines or put multiple layers on the same line
23+
2124
```
2225
[layer type] [layer name] [input count] [output count] [input blobs] [output blobs] [layer specific params]
2326
```

src/paramdict.cpp

Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -359,34 +359,71 @@ static bool vstr_to_float(const char* p, float& value)
359359
return true;
360360
}
361361

362-
static int scan_numeric_value(const DataReader& dr, char vstr[128], bool comma = false)
362+
static bool param_space(char c)
363363
{
364-
if (dr.scan(comma ? ",%127[^, \t\r\n\v\f]" : "%127[^, \t\r\n\v\f]", vstr) != 1)
365-
return 0;
364+
return c == ' ' || c == '\t' || c == '\v' || c == '\f';
365+
}
366366

367-
// a field-width limit must not silently split a numeric token
368-
char extra[2];
369-
if (strlen(vstr) == 127 && dr.scan("%1[^, \t\r\n\v\f]", extra) == 1)
370-
return -1;
367+
static int scan_numeric_value(const char*& p, char vstr[128], bool comma = false)
368+
{
369+
if (comma)
370+
{
371+
if (*p != ',')
372+
return 0;
373+
p++;
374+
}
371375

372-
return 1;
376+
int len = 0;
377+
while (*p && *p != ',' && !param_space(*p))
378+
{
379+
if (len == 127)
380+
return -1;
381+
vstr[len++] = *p++;
382+
}
383+
vstr[len] = '\0';
384+
return len > 0 ? 1 : 0;
373385
}
374386

375387
int ParamDict::load_param(const DataReader& dr)
376388
{
377389
clear();
378390

379-
// require '=' separately: a successful numeric conversion alone does not validate "id="
380-
char idstr[16];
381-
while (dr.scan(" %15[-+0123456789]", idstr) == 1)
391+
// each layer occupies one line
392+
// leave the newline for the next layer header scan
393+
char line[1024] = {0};
394+
std::vector<char> long_line;
395+
while (dr.scan("%1023[^\r\n]", line) == 1)
382396
{
397+
const size_t len = strlen(line);
398+
if (long_line.empty() && len < sizeof(line) - 1)
399+
break;
400+
long_line.insert(long_line.end(), line, line + len);
401+
if (len < sizeof(line) - 1)
402+
break;
403+
}
404+
if (!long_line.empty())
405+
long_line.push_back('\0');
406+
const char* p = long_line.empty() ? line : long_line.data();
407+
408+
while (1)
409+
{
410+
while (param_space(*p))
411+
p++;
412+
if (!*p)
413+
break;
414+
415+
char idstr[16];
416+
int idlen = 0;
417+
while ((*p == '-' || *p == '+' || (*p >= '0' && *p <= '9')) && idlen < 15)
418+
idstr[idlen++] = *p++;
419+
idstr[idlen] = '\0';
383420
int id;
384-
char delimiter[2];
385-
if (!vstr_to_int(idstr, id) || dr.scan("%1[=]", delimiter) != 1)
421+
if (!vstr_to_int(idstr, id) || *p != '=')
386422
{
387423
NCNN_LOGE("ParamDict invalid parameter id or missing equals sign");
388424
return -1;
389425
}
426+
p++;
390427

391428
const bool old_array = id <= -23300;
392429
if (old_array)
@@ -402,7 +439,7 @@ int ParamDict::load_param(const DataReader& dr)
402439
{
403440
char vstr[128];
404441
int len;
405-
if (scan_numeric_value(dr, vstr) != 1 || !vstr_to_int(vstr, len) || !valid_array_length((size_t)len))
442+
if (scan_numeric_value(p, vstr) != 1 || !vstr_to_int(vstr, len) || !valid_array_length((size_t)len))
406443
{
407444
NCNN_LOGE("ParamDict invalid array length (id=%d)", id);
408445
return -1;
@@ -418,7 +455,7 @@ int ParamDict::load_param(const DataReader& dr)
418455
bool is_float = false;
419456
for (int j = 0; j < len; j++)
420457
{
421-
if (scan_numeric_value(dr, vstr, true) != 1)
458+
if (scan_numeric_value(p, vstr, true) != 1)
422459
{
423460
NCNN_LOGE("ParamDict read array element failed");
424461
return -1;
@@ -433,7 +470,7 @@ int ParamDict::load_param(const DataReader& dr)
433470
}
434471
}
435472
// extra elements are not a new parameter or the next layer
436-
if (dr.scan("%1[,]", delimiter) == 1)
473+
if (*p == ',')
437474
{
438475
NCNN_LOGE("ParamDict array length mismatch (id=%d)", id);
439476
return -1;
@@ -444,26 +481,25 @@ int ParamDict::load_param(const DataReader& dr)
444481
continue;
445482
}
446483

447-
char first[2];
448-
if (dr.scan("%1[\"a-zA-Z]", first) == 1)
484+
if (*p == '\"' || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
449485
{
450486
char text[256] = {0};
451-
const bool quoted = first[0] == '\"';
487+
const bool quoted = *p == '\"';
488+
if (quoted)
489+
p++;
490+
int len = 0;
491+
while (*p && (quoted ? *p != '\"' : !param_space(*p)) && len < 255)
492+
text[len++] = *p++;
452493
if (quoted)
453494
{
454-
dr.scan("%255[^\"\r\n]", text);
455-
if (dr.scan("%1[\"]", delimiter) != 1)
495+
if (*p != '\"')
456496
{
457497
NCNN_LOGE("ParamDict unterminated or too long string (id=%d)", id);
458498
return -1;
459499
}
500+
p++;
460501
}
461-
else
462-
{
463-
text[0] = first[0];
464-
dr.scan("%254[^ \t\r\n\v\f]", text + 1);
465-
}
466-
if (dr.scan("%1[^ \t\r\n\v\f]", delimiter) == 1)
502+
if (*p && !param_space(*p))
467503
{
468504
NCNN_LOGE("ParamDict invalid string suffix or string too long (id=%d)", id);
469505
return -1;
@@ -475,7 +511,7 @@ int ParamDict::load_param(const DataReader& dr)
475511
}
476512

477513
char vstr[128];
478-
if (scan_numeric_value(dr, vstr) != 1)
514+
if (scan_numeric_value(p, vstr) != 1)
479515
{
480516
NCNN_LOGE("ParamDict read value failed");
481517
return -1;
@@ -490,27 +526,27 @@ int ParamDict::load_param(const DataReader& dr)
490526
return -1;
491527
}
492528

493-
if (dr.scan("%1[,]", delimiter) == 1)
529+
if (*p == ',')
494530
{
495-
Mat values(16);
496-
if (values.empty())
497-
{
498-
NCNN_LOGE("ParamDict array allocation failed (id=%d)", id);
499-
return -1;
500-
}
531+
p++;
532+
// keep short arrays on the stack until their final Mat allocation
533+
unsigned char local_values[16 * sizeof(float)];
534+
Mat values;
535+
unsigned char* data = local_values;
536+
int capacity = 16;
501537
int len = 1;
502538
if (is_float)
503-
((float*)values)[0] = f;
539+
memcpy(data, &f, sizeof(float));
504540
else
505-
((int*)values)[0] = i;
541+
memcpy(data, &i, sizeof(int));
506542

507543
while (1)
508544
{
509-
const int nscan = scan_numeric_value(dr, vstr);
545+
const int nscan = scan_numeric_value(p, vstr);
510546
// a trailing comma is the established syntax for a one-element array
511547
if (nscan == 0)
512548
{
513-
if (dr.scan("%1[,]", delimiter) == 1)
549+
if (*p == ',')
514550
{
515551
NCNN_LOGE("ParamDict missing array element (id=%d, index=%d)", id, len);
516552
return -1;
@@ -522,26 +558,29 @@ int ParamDict::load_param(const DataReader& dr)
522558
NCNN_LOGE("ParamDict invalid array element (id=%d, index=%d)", id, len);
523559
return -1;
524560
}
525-
if (len == values.w)
561+
if (len == capacity)
526562
{
527-
const size_t capacity = std::min((size_t)values.w * 2, max_array_length());
528-
Mat grown((int)capacity);
563+
const size_t next_capacity = std::min((size_t)capacity * 2, max_array_length());
564+
Mat grown((int)next_capacity);
529565
if (grown.empty())
530566
{
531567
NCNN_LOGE("ParamDict array allocation failed (id=%d)", id);
532568
return -1;
533569
}
534-
memcpy(grown.data, values.data, (size_t)len * sizeof(float));
570+
memcpy(grown.data, data, (size_t)len * sizeof(float));
535571
values = grown;
572+
data = (unsigned char*)values.data;
573+
capacity = (int)next_capacity;
536574
}
537575
if (is_float)
538-
((float*)values)[len] = f;
576+
memcpy(data + (size_t)len * sizeof(float), &f, sizeof(float));
539577
else
540-
((int*)values)[len] = i;
578+
memcpy(data + (size_t)len * sizeof(int), &i, sizeof(int));
541579
len++;
542580

543-
if (dr.scan("%1[,]", delimiter) != 1)
581+
if (*p != ',')
544582
break;
583+
p++;
545584
}
546585

547586
if (len == values.w)
@@ -556,7 +595,7 @@ int ParamDict::load_param(const DataReader& dr)
556595
NCNN_LOGE("ParamDict array allocation failed (id=%d)", id);
557596
return -1;
558597
}
559-
memcpy(v.data, values.data, (size_t)len * sizeof(float));
598+
memcpy(v.data, data, (size_t)len * sizeof(float));
560599
d->params[id].v = v;
561600
}
562601
d->params[id].type = is_float ? 6 : 5;

tests/test_paramdict.cpp

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ static int test_paramdict_invalid_text()
839839
static int test_paramdict_text_boundaries()
840840
{
841841
ParamDictTest pd;
842-
const char* text = "0=-2147483648\t1=2147483647\r\n2=\"\" 3=\" \" 4=1.0,2.0,-3.0 5=7, -23306=2,1.0,2.0 -23307=0 8=0e9999999999 9=1e-9999999999 10=.5 11=4294967296.0";
842+
const char* text = "0=-2147483648\t1=2147483647 2=\"\" 3=\" \" 4=1.0,2.0,-3.0 5=7, -23306=2,1.0,2.0 -23307=0 8=0e9999999999 9=1e-9999999999 10=.5 11=4294967296.0";
843843
if (check_text_result(text, true) || pd.load_param(text))
844844
return -1;
845845
if (pd.get(0, 0) != INT_MIN || pd.get(1, 0) != INT_MAX
@@ -884,8 +884,35 @@ static int test_paramdict_text_boundaries()
884884
}
885885
}
886886

887-
// exercise array growth and the final copy for both element types
888-
const int array_lengths[] = {16, 17, 32, 33, 257};
887+
// lines may end at a read-buffer boundary or split a quoted/numeric token across it
888+
const int line_lengths[] = {1022, 1023, 1024, 1035, 1048, 2046, 2047};
889+
for (size_t j = 0; j < sizeof(line_lengths) / sizeof(line_lengths[0]); j++)
890+
for (int leading = 0; leading < 2; leading++)
891+
{
892+
const char* params = "0=\"hello world\" 1=1.25,2.5 2=42";
893+
const std::string padding = make_param_string(line_lengths[j] - strlen(params), ' ');
894+
std::string input = leading ? padding : std::string(params);
895+
input += leading ? std::string(params) : padding;
896+
input += "\r\n+Probe next 1 1 in out\n";
897+
const unsigned char* ptr = (const unsigned char*)input.c_str();
898+
ncnn::DataReaderFromMemory dr(ptr);
899+
if (pd.load_param(dr) || pd.get(0, std::string()) != "hello world" || pd.get(2, 0) != 42)
900+
{
901+
fprintf(stderr, "ParamDict long line failed len=%d leading=%d\n", line_lengths[j], leading);
902+
return -1;
903+
}
904+
const ncnn::Mat values = pd.get(1, ncnn::Mat());
905+
char header[64];
906+
if (values.w != 2 || values[0] != 1.25f || values[1] != 2.5f
907+
|| dr.scan(" %63[^\r\n]", header) != 1 || strcmp(header, "+Probe next 1 1 in out"))
908+
{
909+
fprintf(stderr, "ParamDict long line array or next header failed\n");
910+
return -1;
911+
}
912+
}
913+
914+
// exercise stack storage, array growth and the final copy for both element types
915+
const int array_lengths[] = {1, 15, 16, 17, 31, 32, 33, 257, 1024};
889916
for (size_t j = 0; j < sizeof(array_lengths) / sizeof(array_lengths[0]); j++)
890917
for (int floating = 0; floating < 2; floating++)
891918
{
@@ -896,6 +923,8 @@ static int test_paramdict_text_boundaries()
896923
snprintf(value, sizeof(value), "%s%d%s", i ? "," : "", i, floating ? ".0" : "");
897924
input += value;
898925
}
926+
if (array_lengths[j] == 1)
927+
input += ",";
899928
input += " 1=42";
900929
if (check_text_result(input.c_str(), true) || pd.load_param(input.c_str()) || pd.get(1, 0) != 42)
901930
{
@@ -952,7 +981,7 @@ static int check_float_boundary(const char* text, float expected, int count, boo
952981

953982
static int test_paramdict_float_boundaries()
954983
{
955-
// combining the comma scan must retain the full numeric token limit
984+
// old array elements retain the full numeric token limit
956985
for (int len = 127; len <= 128; len++)
957986
{
958987
std::string text = "-23300=1,0.";
@@ -1072,9 +1101,9 @@ static int check_paramdict_reload(const ncnn::DataReader& dr, bool binary)
10721101
return -1;
10731102
}
10741103

1075-
// leave each following layer header for the caller, including after a failed scan
1076-
char header[64];
1077-
if (!binary && (dr.scan(" %63[^\r\n]", header) != 1 || strcmp(header, "ReLU next 1 1 in out")))
1104+
// read each following header without consuming its parameters on the same line
1105+
char header[64] = {0};
1106+
if (!binary && (dr.scan(" %20c", header) != 1 || strcmp(header, "ReLU next 1 1 in out")))
10781107
{
10791108
fprintf(stderr, "ParamDict next layer header failed\n");
10801109
return -1;
@@ -1086,7 +1115,7 @@ static int check_paramdict_reload(const ncnn::DataReader& dr, bool binary)
10861115
return -1;
10871116
}
10881117

1089-
if (!binary && (dr.scan(" %63[^\r\n]", header) != 1 || strcmp(header, "Clip last 1 1 out final")))
1118+
if (!binary && (dr.scan(" %23c", header) != 1 || strcmp(header, "Clip last 1 1 out final")))
10901119
{
10911120
fprintf(stderr, "ParamDict last layer header failed\n");
10921121
return -1;
@@ -1107,8 +1136,8 @@ static int check_paramdict_reload(const ncnn::DataReader& dr, bool binary)
11071136

11081137
static int test_paramdict_reload()
11091138
{
1110-
// a literal '-' in the id scanset must not consume punctuation in layer types
1111-
const char* headers[] = {".Custom next 1 1 in out", "/Custom next 1 1 in out", ",Custom next 1 1 in out"};
1139+
// the next layer name is independent of parameter syntax, even with no parameters
1140+
const char* headers[] = {".Custom next 1 1 in out", "/Custom next 1 1 in out", ",Custom next 1 1 in out", "+Probe next 1 1 in out", "-Probe next 1 1 in out", "123Probe next 1 1 in out"};
11121141
for (size_t i = 0; i < sizeof(headers) / sizeof(headers[0]); i++)
11131142
for (int empty = 0; empty < 2; empty++)
11141143
{
@@ -1136,8 +1165,8 @@ static int test_paramdict_reload()
11361165
}
11371166
}
11381167

1139-
const char* text = "0=42 1=1.5 2=3,4 3=hello 31=31\r\nReLU next 1 1 in out\r\n4=7\r\nClip last 1 1 out final\r\n";
1140-
const char* old_array_text = "0=42 1=1.5 -23302=2,3,4 3=hello 31=31\r\nReLU next 1 1 in out\r\n4=7\r\nClip last 1 1 out final\r\n";
1168+
const char* text = "0=42 1=1.5 2=3,4 3=hello 31=31\r\nReLU next 1 1 in out 4=7\r\nClip last 1 1 out final\r\n";
1169+
const char* old_array_text = "0=42 1=1.5 -23302=2,3,4 3=hello 31=31\r\nReLU next 1 1 in out 4=7\r\nClip last 1 1 out final\r\n";
11411170

11421171
// three consecutive parameter blocks, with no parameters in the last block
11431172
const int words[] = {0, 42, 1, 0x3fc00000, -23302, 2, 3, 4, -23403, 5, 0x6c6c6568, 0x6f, 31, 31, -233, 4, 7, -233, -233};

0 commit comments

Comments
 (0)