// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Tag identifier (four bytes).
pub struct Tag {
value : UInt
} derive(Eq, Show, ToJson, Hash)
///|
/// Construct a tag from four bytes.
pub fn Tag::from_bytes(b1 : Byte, b2 : Byte, b3 : Byte, b4 : Byte) -> Tag {
let value = (b1.to_uint() << 24) |
(b2.to_uint() << 16) |
(b3.to_uint() << 8) |
b4.to_uint()
Tag::{ value, }
}
///|
/// Construct a tag from four characters.
pub fn Tag::from_chars(c1 : Char, c2 : Char, c3 : Char, c4 : Char) -> Tag {
Tag::from_bytes(
c1.to_uint().to_byte(),
c2.to_uint().to_byte(),
c3.to_uint().to_byte(),
c4.to_uint().to_byte(),
)
}
///|
/// Construct a tag from a string, padding with spaces when shorter than 4.
pub fn Tag::from_string(s : String, len? : Int = -1) -> Tag {
if s is "" || len == 0 {
return tag_none
}
let max_len = if len < 0 || len > 4 { 4 } else { len }
let bytes : Array[Byte] = []
let mut count = 0
for c in s {
if count >= max_len {
break
}
bytes.push(c.to_uint().to_byte())
count = count + 1
}
for _ in bytes.length()..<4 {
bytes.push(b' ')
}
Tag::from_bytes(bytes[0], bytes[1], bytes[2], bytes[3])
}
///|
/// Extract the four bytes of the tag, in big-endian order.
pub fn Tag::bytes(self : Tag) -> (Byte, Byte, Byte, Byte) {
let value = self.value
(
((value >> 24) & 0xffU).to_byte(),
((value >> 16) & 0xffU).to_byte(),
((value >> 8) & 0xffU).to_byte(),
(value & 0xffU).to_byte(),
)
}
///|
/// Convert a tag to a four-character string.
pub fn Tag::to_string(self : Tag) -> String {
let (b1, b2, b3, b4) = self.bytes()
let sb = StringBuilder::new(size_hint=4)
sb
..write_char(b1.to_int().unsafe_to_char())
..write_char(b2.to_int().unsafe_to_char())
..write_char(b3.to_int().unsafe_to_char())
..write_char(b4.to_int().unsafe_to_char())
sb.to_string()
}
///|
/// Return the raw 32-bit value of the tag.
pub fn Tag::to_uint(self : Tag) -> UInt {
self.value
}
///|
/// Unset tag.
pub let tag_none : Tag = Tag::from_bytes(b'\x00', b'\x00', b'\x00', b'\x00')
///|
/// Maximum possible unsigned tag.
pub let tag_max : Tag = Tag::from_bytes(b'\xFF', b'\xFF', b'\xFF', b'\xFF')
///|
/// Maximum possible signed tag.
pub let tag_max_signed : Tag = Tag::from_bytes(
b'\x7F', b'\xFF', b'\xFF', b'\xFF',
)