• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moonbitlang / x / 301

10 Dec 2024 06:19AM UTC coverage: 85.204% (-2.6%) from 87.841%
301

Pull #78

github

web-flow
Merge b830031f4 into 91f0fdf48
Pull Request #78: feat: new package encoding

105 of 161 new or added lines in 3 files covered. (65.22%)

124 existing lines in 29 files now uncovered.

1169 of 1372 relevant lines covered (85.2%)

434.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.36
/uuid/uuid.mbt
1
// Copyright 2024 International Digital Economy Academy
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
///| UUID that complies with RFC 4122.
16
///
17
/// UUID objects are immutable, hashable and usable as mapping keys.
18
/// The Display of UUIDs is like `12345678-1234-1234-1234-123456789abc`;
19
/// UUIDs can be constructed using such strings, or 16-bytes strings with big-
20
/// endian byte order.
21
///
22
/// # Usage
23
///
24
/// ```moonbit
25
/// let u = @uuid.from_hex("ddf99703-742f-7505-4c54-df36a9c243fe")
26
/// let u2 = @uuid.from_bytes(Bytes.of([221, 249, 151, 3, 116, 47, 117, 5, 76, 84, 223, 54, 169, 194, 67, 254]))
27
/// // assert_eq(u, u2) should be true
28
/// ```
29
///
30
/// Construct an RFC-4122-compliant v4 UUID:
31
///
32
/// ```moonbit
33
/// let u = @uuid
34
///   .from_hex("9558cfadb48547c70f9adf1f75cdef29")
35
///   .as_version(@uuid.V4)
36
/// ```
37
///
38
struct UUID {
39
  // XXX: replace with an 128-bit data structure when there is one
40
  // use the derived hash
41
  hi : Int64
42
  lo : Int64
43
} derive(Eq, Compare)
44

45
///| Constructs a UUID with the given big-endian bytes.
46
///
47
/// The produced UUID is not RFC-4122 compliant; see also `as_version()`.
48
///
49
pub fn from_bytes(bytes : Bytes) -> UUID!Error {
50
  if bytes.length() != 16 {
3✔
51
    fail!("requires 16 bytes")
×
52
  }
53
  // XXX: this is ideally Int64::from_bytes() (needs a cheap slicing method)
54
  let hi = for rv = 0L, i = 0; i < 8; {
9✔
55
    continue rv | (bytes[i].to_int64() << ((7 - i) * 8)), i + 1
8✔
56
  } else {
57
    rv
1✔
58
  }
59
  let lo = for rv = 0L, i = 8; i < 16; {
9✔
60
    continue rv | (bytes[i].to_int64() << ((15 - i) * 8)), i + 1
8✔
61
  } else {
62
    rv
1✔
63
  }
64
  { hi, lo }
65
}
66

67
///| The UUID as a 16-byte string.
68
pub fn to_bytes(self : UUID) -> Bytes {
69
  // XXX: replace with a cheaper way
70
  let rv = Bytes::new(16)
1✔
71
  for i = 0; i < 8; i = i + 1 {
8✔
72
    rv[i] = (self.hi.reinterpret_as_uint64() >> ((7 - i) * 8)).to_byte()
8✔
73
  }
74
  for i = 8; i < 16; i = i + 1 {
8✔
75
    rv[i] = (self.lo.reinterpret_as_uint64() >> ((15 - i) * 8)).to_byte()
8✔
76
  }
77
  rv
78
}
79

80
test "bytes" {
81
  let buf = Bytes::from_array(
82
    [201, 130, 251, 104, 223, 80, 76, 161, 145, 248, 186, 162, 4, 9, 229, 185].map(
83
      Int::to_byte,
84
    ),
85
  )
86
  inspect!(
87
    from_bytes!(buf).to_bytes(),
88
    content=
89
      #|b"\xc9\x82\xfb\x68\xdf\x50\x4c\xa1\x91\xf8\xba\xa2\x04\x09\xe5\xb9"
90
    ,
91
  )
92
  inspect!(
93
    from_bytes?(Bytes::new(15)),
94
    content="Err(moonbitlang/core/builtin.Failure.Failure)",
95
  )
96
  inspect!(
97
    from_bytes?(Bytes::new(17)),
98
    content="Err(moonbitlang/core/builtin.Failure.Failure)",
99
  )
100
}
101

102
///|
103
fn hex_char_to_int64(c : Char) -> Int64!Error {
104
  let d = if '0' <= c && c <= '9' {
344✔
105
    c.to_int() - '0'.to_int()
231✔
106
  } else if 'a' <= c && c <= 'f' {
113✔
107
    c.to_int() - 'a'.to_int() + 10
107✔
108
  } else if 'A' <= c && c <= 'F' {
6✔
109
    c.to_int() - 'A'.to_int() + 10
6✔
110
  } else {
UNCOV
111
    fail!("invalid syntax")
×
112
  }
113
  d.to_int64()
114
}
115

116
///| Constructs a UUID with the given hexadecimal string.
117
///
118
/// This function ignores dashes (`-`) in the given string. The remaining
119
/// letters must be exactly 32 hexadecimals, or an error will be raised.
120
///
121
pub fn from_hex(hex : String) -> UUID!Error {
122
  // XXX: ideally we should use strconv here, but we don't have sufficient
123
  // tooling around String yet
124
  let (hi, i) = for hi = 0L, i = 0, n = 0; n < 16; {
12✔
125
    if i >= hex.length() {
203✔
UNCOV
126
      fail!("not enough hexadecimal in UUID string")
×
127
    }
128
    let c = hex[i]
129
    if c == '-' {
130
      continue hi, i + 1, n
22✔
131
    } else {
132
      let d = hex_char_to_int64!(c)
180✔
133
      continue hi | (d << ((15 - n) * 4)), i + 1, n + 1
180✔
134
    }
135
  } else {
136
    (hi, i)
11✔
137
  }
138
  let (lo, i) = for lo = 0L, i = i, n = 0; n < 16; {
196✔
139
    if i >= hex.length() {
186✔
UNCOV
140
      fail!("not enough hexadecimal in UUID string")
×
141
    }
142
    let c = hex[i]
143
    if c == '-' {
144
      continue lo, i + 1, n
21✔
145
    } else {
146
      let d = hex_char_to_int64!(c)
164✔
147
      continue lo | (d << ((15 - n) * 4)), i + 1, n + 1
164✔
148
    }
149
  } else {
150
    (lo, i)
10✔
151
  }
152
  if i == hex.length() {
153
    { hi, lo }
10✔
154
  } else {
UNCOV
155
    fail!("badly formed hexadecimal UUID string")
×
156
  }
157
}
158

159
///|
160
let hex_table : Array[Int] = "0123456789abcdef"
161
  .to_array()
162
  .map(fn { c => c.to_int() })
32✔
163

164
///|
165
let dash : Int = '-'.to_int()
166

167
///|
168
let segments = [8, 4, 4, 0, 4, 12]
169

170
// XXX: use sum() instead; weird `moon fmt`
171
///|
172
let size : Int = (
173
    segments.fold(fn { acc, x => acc + x }, init=0) + segments.length() - 2
12✔
174
  ) *
175
  2
176

177
///|
178
pub fn to_string(self : UUID) -> String {
179
  // This is a manually-composed UTF-16 string
180
  let rv = Bytes::new(size)
2✔
181
  for v = self.lo, c = size - 1, i = size - 1, d = segments.length() - 1 {
182
    let s = segments[d] * 2
76✔
183
    if s == 0 {
184
      continue self.hi, c, i, d - 1
2✔
185
    } else if i > c - s {
74✔
186
      rv[i - 1] = hex_table[(v & 0xfL).to_int()].to_byte()
64✔
187
      continue (v.reinterpret_as_uint64() >> 4).reinterpret_as_int64(),
188
        c,
189
        i - 2,
190
        d
191
    } else if i > 0 {
10✔
192
      rv[i - 1] = dash.to_byte()
8✔
193
      continue v, i - 2, i - 2, d - 1
194
    } else {
195
      break rv.to_unchecked_string()
2✔
196
    }
197
  }
198
}
199

200
///|
201
pub impl Show for UUID with output(self : UUID, logger : Logger) -> Unit {
202
  logger.write_string(self.to_string())
2✔
203
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc