Skip to content

Commit 5c79a48

Browse files
committed
Fix #28
1 parent 28a0c5e commit 5c79a48

File tree

5 files changed

+12586
-12462
lines changed

5 files changed

+12586
-12462
lines changed

doc/libjsonnet.js

+12,456-12,456
Large diffs are not rendered by default.

doc/stdlib.html

+21-2
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,30 @@ <h4>std.flattenArrays(arrs)</h4>
497497
<p>Concatenate an array of arrays into a single array.</p>
498498

499499

500-
<h3>miscellaneous</h3>
500+
<h3>Base 64</h3>
501+
502+
<h4>std.base64(v)</h4>
503+
504+
<p>Encodes the given value into a base64 string. The encoding sequence is A-Za-z0-9+/ with = to pad
505+
the output to a multiple of 4 characters. The value can be a string or an array of numbers, but the
506+
codepoints / numbers must be in the 0 to 255 range. The resulting string has no line breaks.</p>
507+
508+
<h4>std.base64DecodeBytes(s)</h4>
509+
510+
<p>Decodes the given base64 string into an array of bytes (number values). Currently assumes the
511+
input string has no linebreaks and is padded to a multiple of 4 (with the = character). In other
512+
words, it consumes the output of std.base64().</p>
513+
514+
<h4>std.base64Decode(s)</h4>
515+
516+
<p>Behaves like std.base64DecodeBytes() except returns a string instead of an array of bytes.</p>
517+
518+
519+
<h3>Miscellaneous</h3>
501520

502521
<h4>std.force(x)</h4>
503522

504-
<p>always returns true, but always evaluates its argument. this allows controlling memory usage in
523+
<p>Always returns true, but always evaluates its argument. this allows controlling memory usage in
505524
recursive algorithms, and causes runtime errors in x to be discovered immediately.</p>
506525

507526

doc/stdlib.html.jinja

+21-2
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,30 @@ is suitable for constructing bash scripts and the like.</p>
311311
<p>Concatenate an array of arrays into a single array.</p>
312312

313313

314-
<h3>miscellaneous</h3>
314+
<h3>Base 64</h3>
315+
316+
<h4>std.base64(v)</h4>
317+
318+
<p>Encodes the given value into a base64 string. The encoding sequence is A-Za-z0-9+/ with = to pad
319+
the output to a multiple of 4 characters. The value can be a string or an array of numbers, but the
320+
codepoints / numbers must be in the 0 to 255 range. The resulting string has no line breaks.</p>
321+
322+
<h4>std.base64DecodeBytes(s)</h4>
323+
324+
<p>Decodes the given base64 string into an array of bytes (number values). Currently assumes the
325+
input string has no linebreaks and is padded to a multiple of 4 (with the = character). In other
326+
words, it consumes the output of std.base64().</p>
327+
328+
<h4>std.base64Decode(s)</h4>
329+
330+
<p>Behaves like std.base64DecodeBytes() except returns a string instead of an array of bytes.</p>
331+
332+
333+
<h3>Miscellaneous</h3>
315334

316335
<h4>std.force(x)</h4>
317336

318-
<p>always returns true, but always evaluates its argument. this allows controlling memory usage in
337+
<p>Always returns true, but always evaluates its argument. this allows controlling memory usage in
319338
recursive algorithms, and causes runtime errors in x to be discovered immediately.</p>
320339

321340

std.jsonnet

+79-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ limitations under the License.
8282
map(func, arr)::
8383
if std.type(func) != "function" then
8484
error("std.map first param must be function, got " + std.type(func))
85-
else if std.type(arr) != "array" then
86-
error("std.map second param must be array, got " + std.type(arr))
85+
else if std.type(arr) != "array" && std.type(arr) != "string" then
86+
error("std.map second param must be array / string, got " + std.type(arr))
8787
else
8888
std.makeArray(std.length(arr), function(i) func(arr[i])),
8989

@@ -685,4 +685,81 @@ limitations under the License.
685685
local vars = ["%s = %s" % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];
686686
std.join("\n", vars + [""]),
687687

688+
689+
local base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
690+
local base64_inv = {[base64_table[i]]: i for i in std.range(0, 63)},
691+
692+
base64(input)::
693+
local bytes =
694+
if std.type(input) == "string" then
695+
std.map(function(c) std.codepoint(c), input)
696+
else
697+
input;
698+
699+
local aux(arr, i, r) =
700+
if i >= std.length(arr) then
701+
r
702+
else if i + 1 >= std.length(arr) then
703+
local str =
704+
// 6 MSB of i
705+
base64_table[(arr[i] & 252) >> 2] +
706+
// 2 LSB of i
707+
base64_table[(arr[i] & 3) << 4] +
708+
"==";
709+
aux(arr, i + 3, r + str)
710+
else if i + 2 >= std.length(arr) then
711+
local str =
712+
// 6 MSB of i
713+
base64_table[(arr[i] & 252) >> 2] +
714+
// 2 LSB of i, 4 MSB of i+1
715+
base64_table[(arr[i] & 3) << 4 | (arr[i+1] & 240) >> 4] +
716+
// 4 LSB of i+1
717+
base64_table[(arr[i+1] & 15) << 2] +
718+
"=";
719+
aux(arr, i + 3, r + str)
720+
else
721+
local str =
722+
// 6 MSB of i
723+
base64_table[(arr[i] & 252) >> 2] +
724+
// 2 LSB of i, 4 MSB of i+1
725+
base64_table[(arr[i] & 3) << 4 | (arr[i+1] & 240) >> 4] +
726+
// 4 LSB of i+1, 2 MSB of i+2
727+
base64_table[(arr[i+1] & 15) << 2 | (arr[i+2] & 192) >> 6] +
728+
// 6 LSB of i+2
729+
base64_table[(arr[i+2] & 63)];
730+
aux(arr, i + 3, r + str);
731+
732+
local sanity = std.foldl(function(r, a) r && (a < 256), bytes, true);
733+
if !sanity then
734+
error "Can only base64 encode strings / arrays of single bytes."
735+
else
736+
aux(bytes, 0, ""),
737+
738+
739+
base64DecodeBytes(str)::
740+
if std.length(str) % 4 != 0 then
741+
error "Not a base64 encoded string \"%s\"" % str
742+
else
743+
local aux(str, i, r) =
744+
if i >= std.length(str) then
745+
r
746+
else
747+
// all 6 bits of i, 2 MSB of i+1
748+
local n1 = [base64_inv[str[i]] << 2 | (base64_inv[str[i+1]] >> 4)];
749+
// 4 LSB of i+1, 4MSB of i+2
750+
local n2 =
751+
if str[i+2] == "=" then []
752+
else [(base64_inv[str[i+1]] & 15) << 4 | (base64_inv[str[i+2]] >> 2)];
753+
// 2 LSB of i+2, all 6 bits of i+3
754+
local n3 =
755+
if str[i+3] == "=" then []
756+
else [(base64_inv[str[i+2]] & 3) << 6 | base64_inv[str[i+3]]];
757+
aux(str, i+4, r + n1 + n2 + n3);
758+
aux(str, 0, []),
759+
760+
base64Decode(str)::
761+
local bytes = std.base64DecodeBytes(str);
762+
std.join("", std.map(function(b) std.char(b), bytes))
763+
764+
688765
}

test_suite/stdlib.jsonnet

+9
Original file line numberDiff line numberDiff line change
@@ -234,5 +234,14 @@ std.assertEqual(std.manifestPythonVars({
234234
""
235235
])) &&
236236

237+
std.assertEqual(std.base64("Hello World!"), "SGVsbG8gV29ybGQh") &&
238+
std.assertEqual(std.base64("Hello World"), "SGVsbG8gV29ybGQ=") &&
239+
std.assertEqual(std.base64("Hello Worl"), "SGVsbG8gV29ybA==") &&
240+
std.assertEqual(std.base64(""), "") &&
241+
242+
std.assertEqual(std.base64Decode("SGVsbG8gV29ybGQh"), "Hello World!") &&
243+
std.assertEqual(std.base64Decode("SGVsbG8gV29ybGQ="), "Hello World") &&
244+
std.assertEqual(std.base64Decode("SGVsbG8gV29ybA=="), "Hello Worl") &&
245+
std.assertEqual(std.base64Decode(""), "") &&
237246

238247
true

0 commit comments

Comments
 (0)