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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use crate::fact::core_types::CoreTypes;
use crate::MemoryIndex;
use serde_derive::{Deserialize, Serialize};
use wasm_encoder::{EntityType, ValType};
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
pub struct Transcoder {
pub from_memory: MemoryIndex,
pub from_memory64: bool,
pub to_memory: MemoryIndex,
pub to_memory64: bool,
pub op: Transcode,
}
/// Possible transcoding operations that must be provided by the host.
///
/// Note that each transcoding operation may have a unique signature depending
/// on the precise operation.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum Transcode {
Copy(FixedEncoding),
Latin1ToUtf16,
Latin1ToUtf8,
Utf16ToCompactProbablyUtf16,
Utf16ToCompactUtf16,
Utf16ToLatin1,
Utf16ToUtf8,
Utf8ToCompactUtf16,
Utf8ToLatin1,
Utf8ToUtf16,
}
impl Transcode {
/// Get this transcoding's symbol fragment.
pub fn symbol_fragment(&self) -> &'static str {
match self {
Transcode::Copy(x) => match x {
FixedEncoding::Utf8 => "copy_utf8",
FixedEncoding::Utf16 => "copy_utf16",
FixedEncoding::Latin1 => "copy_latin1",
},
Transcode::Latin1ToUtf16 => "latin1_to_utf16",
Transcode::Latin1ToUtf8 => "latin1_to_utf8",
Transcode::Utf16ToCompactProbablyUtf16 => "utf16_to_compact_probably_utf16",
Transcode::Utf16ToCompactUtf16 => "utf16_to_compact_utf16",
Transcode::Utf16ToLatin1 => "utf16_to_latin1",
Transcode::Utf16ToUtf8 => "utf16_to_utf8",
Transcode::Utf8ToCompactUtf16 => "utf8_to_compact_utf16",
Transcode::Utf8ToLatin1 => "utf8_to_latin1",
Transcode::Utf8ToUtf16 => "utf8_to_utf16",
}
}
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum FixedEncoding {
Utf8,
Utf16,
Latin1,
}
impl Transcoder {
pub fn name(&self) -> String {
format!(
"{} (mem{} => mem{})",
self.op.desc(),
self.from_memory.as_u32(),
self.to_memory.as_u32(),
)
}
pub fn ty(&self, types: &mut CoreTypes) -> EntityType {
let from_ptr = if self.from_memory64 {
ValType::I64
} else {
ValType::I32
};
let to_ptr = if self.to_memory64 {
ValType::I64
} else {
ValType::I32
};
let ty = match self.op {
// These direct transcodings take the source pointer, the source
// code units, and the destination pointer.
//
// The memories being copied between are part of each intrinsic and
// the destination code units are the same as the source.
// Note that the pointers are dynamically guaranteed to be aligned
// and in-bounds for the code units length as defined by the string
// encoding.
Transcode::Copy(_) | Transcode::Latin1ToUtf16 => {
types.function(&[from_ptr, from_ptr, to_ptr], &[])
}
// Transcoding from utf8 to utf16 takes the from ptr/len as well as
// a destination. The destination is valid for len*2 bytes. The
// return value is how many code units were written to the
// destination.
Transcode::Utf8ToUtf16 => types.function(&[from_ptr, from_ptr, to_ptr], &[to_ptr]),
// Transcoding to utf8 as a smaller format takes all the parameters
// and returns the amount of space consumed in the src/destination
Transcode::Utf16ToUtf8 | Transcode::Latin1ToUtf8 => {
types.function(&[from_ptr, from_ptr, to_ptr, to_ptr], &[from_ptr, to_ptr])
}
// The return type is a tagged length which indicates which was
// used
Transcode::Utf16ToCompactProbablyUtf16 => {
types.function(&[from_ptr, from_ptr, to_ptr], &[to_ptr])
}
// The initial step of transcoding from a fixed format to a compact
// format. Takes the ptr/len of the source the the destination
// pointer. The destination length is implicitly the same. Returns
// how many code units were consumed in the source, which is also
// how many bytes were written to the destination.
Transcode::Utf8ToLatin1 | Transcode::Utf16ToLatin1 => {
types.function(&[from_ptr, from_ptr, to_ptr], &[from_ptr, to_ptr])
}
// The final step of transcoding to a compact format when the fixed
// transcode has failed. This takes the ptr/len of the source that's
// remaining to transcode. Then this takes the destination ptr/len
// as well as the destination bytes written so far with latin1.
// Finally this returns the number of code units written to the
// destination.
Transcode::Utf8ToCompactUtf16 | Transcode::Utf16ToCompactUtf16 => {
types.function(&[from_ptr, from_ptr, to_ptr, to_ptr, to_ptr], &[to_ptr])
}
};
EntityType::Function(ty)
}
}
impl Transcode {
/// Returns a human-readable description for this transcoding operation.
pub fn desc(&self) -> &'static str {
match self {
Transcode::Copy(FixedEncoding::Utf8) => "utf8-to-utf8",
Transcode::Copy(FixedEncoding::Utf16) => "utf16-to-utf16",
Transcode::Copy(FixedEncoding::Latin1) => "latin1-to-latin1",
Transcode::Latin1ToUtf16 => "latin1-to-utf16",
Transcode::Latin1ToUtf8 => "latin1-to-utf8",
Transcode::Utf16ToCompactProbablyUtf16 => "utf16-to-compact-probably-utf16",
Transcode::Utf16ToCompactUtf16 => "utf16-to-compact-utf16",
Transcode::Utf16ToLatin1 => "utf16-to-latin1",
Transcode::Utf16ToUtf8 => "utf16-to-utf8",
Transcode::Utf8ToCompactUtf16 => "utf8-to-compact-utf16",
Transcode::Utf8ToLatin1 => "utf8-to-latin1",
Transcode::Utf8ToUtf16 => "utf8-to-utf16",
}
}
}
impl FixedEncoding {
pub(crate) fn width(&self) -> u8 {
match self {
FixedEncoding::Utf8 => 1,
FixedEncoding::Utf16 => 2,
FixedEncoding::Latin1 => 1,
}
}
}