1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#pragma once
/// @file wasm-codec.hh -- codec wire-format encoder for the Nix AST.
///
/// Encodes a post-finalize Expr tree into the lix codec wire format so the Go
/// evaluator can decode it without JSON parsing.
///
/// Wire format recap (see gonix/codec/codec.go):
/// message = total_len ":" value
/// value = record | list | variant | scalar
/// record = "{" *( klen ":" kbytes "," vlen ":" vbytes ";" ) "}"
/// list = "[" *( len ":" item ";" ) "]"
/// variant = "<" taglen ":" tagbytes value
/// scalar = len ":" rawbytes
///
/// Each Expr node encodes as a variant whose tag is the node type name and
/// whose payload is a record of its fields.
#include "lix/libexpr/nixexpr.hh"
#include "lix/libexpr/pos-table.hh"
#include "lix/libexpr/symbol-table.hh"
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
namespace nix::wasm_codec {
// ---- low-level encoding primitives ----
/// len ":" rawbytes
inline std::string encScalar(std::string_view s)
{
return std::to_string(s.size()) + ":" + std::string(s);
}
/// 4 bytes little-endian uint32
inline std::string encUint32(uint32_t v)
{
char buf[4];
memcpy(buf, &v, 4);
return std::string("4:") + std::string(buf, 4);
}
/// 8 bytes little-endian int64
inline std::string encInt64(int64_t v)
{
char buf[8];
memcpy(buf, &v, 8);
return std::string("8:") + std::string(buf, 8);
}
/// 8 bytes little-endian float64
inline std::string encFloat64(double v)
{
char buf[8];
memcpy(buf, &v, 8);
return std::string("8:") + std::string(buf, 8);
}
/// 1 byte boolean
inline std::string encBool(bool b)
{
// Must use the (ptr, len) constructor — "1:\x00" via C-string stops at NUL.
if (b) {
return std::string("1:\x01", 3);
} else {
return std::string("1:\x00", 3);
}
}
/// record field: klen ":" kbytes "," vlen ":" vbytes ";"
inline std::string field(std::string_view key, std::string_view encodedValue)
{
return std::to_string(key.size()) + ":" + std::string(key) + "," + std::to_string(encodedValue.size())
+ ":" + std::string(encodedValue) + ";";
}
/// "{" fields "}"
inline std::string record(std::string_view fields)
{
return "{" + std::string(fields) + "}";
}
/// "<" taglen ":" tag payload
inline std::string variant(std::string_view tag, std::string_view encodedPayload)
{
return "<" + std::to_string(tag.size()) + ":" + std::string(tag) + std::string(encodedPayload);
}
/// "[" *( len ":" item ";" ) "]"
inline std::string list(std::string_view encodedItems)
{
return "[" + std::string(encodedItems) + "]";
}
/// single list item: len ":" item ";"
inline std::string listItem(std::string_view encodedItem)
{
return std::to_string(encodedItem.size()) + ":" + std::string(encodedItem) + ";";
}
/// total_len ":" inner
inline std::string message(std::string_view inner)
{
return std::to_string(inner.size()) + ":" + std::string(inner);
}
// ---- AST encoding ----
/// Encode the given expression tree into the codec wire format.
/// Returns the raw variant-encoded value (no top-level length prefix).
/// Wrap with message() before writing to a response buffer.
/// positions is used to resolve PosIdx values to {file, line, column}.
/// filePath, if non-empty, is used as the file name for positions whose
/// origin is Pos::String (i.e. parsed from a string, not a file on disk).
std::string encodeExpr(
const Expr & e, const SymbolTable & symbols, const PosTable & positions, std::string_view filePath = {}
);
} // namespace nix::wasm_codec
|