nkb boundary
Give the limit a field states and get the values that test it: nothing, one under, exactly at and one over, in characters that weigh differently in bytes and UTF-16 units.
nkb boundary [--max-length N] [--min-length N]
[--unit graphemes|code-points|utf16|bytes]
[--format json|csv|lines] [--escaped|--raw] [--base64]
What does it make?
For a field that takes at most 50 characters: the empty value, and then values of 49, 50 and 51 in each of six fills. With --min-length it adds the lengths around the least the field takes, and a length that two bounds share comes once. Every value says how long it is in four units, and whether its length is inside the limit.
The packs hold lists of odd characters. This command answers the one sentence a specification gives you about a field.
Why six fills?
Because “50 characters” is one of four numbers, and you rarely know which one the application counts:
- A browser’s
maxlengthis measured the way JavaScript measures a string, in UTF-16 units. An emoji is two. - PostgreSQL’s
varchar(n)holds n characters, not bytes. - SQL Server’s
varchar(n)counts bytes, and itsnvarchar(n)counts pairs of bytes. - The person who wrote the specification counts what they can see.
Fifty letters a are fifty in every unit, so they cannot tell these apart. Fifty of a two-byte letter are fifty characters and a hundred bytes, fifty emoji are a hundred UTF-16 units, and fifty family emoji are fifty characters to the eye and two hundred and fifty code points. A field that takes fifty letters a and refuses or cuts one of the others has told you what it counts.
None of the characters changes under a Unicode normal form, so a field that normalises its text still gets the length you asked for.
Examples
One value per line, to paste from or to feed a test:
$ nkb boundary --max-length 50 --format lines > limit-50.txt
nkb boundary: 19 values, 1500 code points, 4350 bytes
CSV, with the four sizes of every value side by side:
$ nkb boundary --max-length 50 --format csv
nkb boundary: 19 values, 1500 code points, 4350 bytes
id,name,value,fill,length,within,graphemes,code_points,utf16_units,bytes
empty,Empty value,,,0,true,0,0,0,0
letters-49,49 x a,aaaaa...,letters,49,true,49,49,49,49
letters-50,50 x a,aaaaa...,letters,50,true,50,50,50,50
letters-51,51 x a,aaaaa...,letters,51,false,51,51,51,51
...
two-byte-50,50 x U+0142,...,two-byte,50,true,50,50,50,100
four-byte-50,50 x U+1F600,...,four-byte,50,true,50,50,100,200
cluster-50,50 x U+1F468 U+200D U+1F469 U+200D U+1F467,...,cluster,50,true,50,250,400,900
A limit counted in bytes. Every value has exactly the bytes its row says, with the letter a in front where whole characters do not add up:
$ nkb boundary --max-length 50 --unit bytes --format csv
...
two-byte-49,1 x a + 24 x U+0142,...,two-byte,49,true,25,25,25,49
two-byte-50,25 x U+0142,...,two-byte,50,true,25,25,25,50
two-byte-51,1 x a + 25 x U+0142,...,two-byte,51,false,26,26,26,51
Which unit should I ask for?
Start without --unit. The values are then counted the way a person counts, and the application’s answer to the heavier fills shows what it counts instead.
Then run again in that unit. A value of 51 bytes made of a two-byte letter is one a and twenty-five letters, so a field that cuts at 50 bytes cuts through the middle of the last letter. In utf16 the same happens to an emoji, and what is left is half a character: text that no longer displays, a save that fails, or an error far from the field. A value made of whole characters up to the limit never provokes this, which is why the padding stands in front.
How do I read a counterstring?
Every star is preceded by the number of its own place. This is the counterstring of 51:
*3*5*7*9*12*15*18*21*24*27*30*33*36*39*42*45*48*51*
If the field kept ...42*45*48*51, the last star stands at 48 and two characters follow it, so 50 arrived. You read the cut from the field itself, without counting and without a second tool.
A counterstring is digits and stars. A field that takes letters only refuses it for that reason, and that is what the letters fill is for. A field that renders Markdown may swallow the stars.
What does “within” promise?
That the length of the value is inside the limit you gave, in the unit you gave, and nothing more. A required field still refuses the empty value, and a field that takes letters only still refuses an emoji. When a value that is within the limit is refused for its length, either the application counts in another unit or the limit is not the one the specification states. Both are worth a report.
Options
Fills
Units
Formats
Exit codes
This command's help names no exit codes of its own. The numbers mean the same for every command: Exit codes
The help, as the program prints it
What nkb boundary --help prints, from nkb 0.3.0.
nkb boundary - make the values that test the length limit of a field
Usage:
nkb boundary [--max-length N] [--min-length N]
[--unit graphemes|code-points|utf16|bytes]
[--format json|csv|lines] [--escaped|--raw] [--base64]
Give the limit the field states and get the values worth trying: nothing,
then one under, exactly at and one over each bound. Every length comes in
six fills, because "50 characters" is a different number in each unit.
Fills:
letters The letter a
counterstring Numbers and stars, as in *3*5*7*9*12*. Each number is the
place of the star after it, so a field that cut the value
shows where it cut
two-byte A letter of two bytes (U+0142)
three-byte A sign of three bytes (U+20AC)
four-byte An emoji of four bytes and two UTF-16 units (U+1F600)
cluster A family emoji: one character to the eye, five code points
Units:
graphemes Characters as a person counts them. The default
code-points Unicode code points
utf16 UTF-16 units, which a browser's maxlength and JavaScript count
bytes Bytes of UTF-8
Counted in graphemes, 50 two-byte letters are 100 bytes and 50 emoji are
100 UTF-16 units, so a field that refuses or cuts them counts something
else. Run again in that unit: a value then has exactly that many units,
padded with the letter a in front, and the limit falls inside the last
character of the value one over it.
Values go to standard output and the account of what came out goes to
standard error. "within" says whether the length of a value is inside the
limit and nothing more: a required field still refuses the empty value.
Formats:
json Both forms of every value, with its fill, its length, whether it
is within the limit and its size in all four units. The default
csv One row per value, with the same columns. Escaped by default
lines One value per line. The empty value is an empty line
Options:
--max-length N The most the field takes, from 1 to 100000
--min-length N The least the field takes, from 1 to 100000
--unit What the limit is counted in (default: graphemes)
--escaped Print values as a pack file stores them
--raw Print values literally
--base64 Encode values, for channels that damage text
-h, --help Show this help and exit with 0