///|
#valtype
struct SmallBytes {
buf : Bytes?
off : UInt64
len : UInt64
}
///|
pub fn bytes(bytes : @bytes.View) -> SmallBytes {
let old_len = bytes.length()
if old_len < 8 {
let off = for i = 0, off = 0UL; i < old_len; {
let b = bytes.unsafe_get(i)
continue i + 1, off | (b.to_uint64() << (i * 8))
} else {
off
}
let len = old_len.to_uint64() << 56
SmallBytes::{ buf: None, off, len }
} else if old_len < 16 {
let new_off = bytes.unsafe_get(0).to_uint64() |
(bytes.unsafe_get(1).to_uint64() << 8) |
(bytes.unsafe_get(2).to_uint64() << 16) |
(bytes.unsafe_get(3).to_uint64() << 24) |
(bytes.unsafe_get(4).to_uint64() << 32) |
(bytes.unsafe_get(5).to_uint64() << 40) |
(bytes.unsafe_get(6).to_uint64() << 48) |
(bytes.unsafe_get(7).to_uint64() << 56)
let new_len = for i = 8, new_len = 0UL; i < old_len; {
let b = bytes.unsafe_get(i)
continue i + 1, new_len | (b.to_uint64() << ((i - 8) * 8))
} else {
new_len
}
let len = (old_len.to_uint64() << 56) | new_len
SmallBytes::{ buf: None, off: new_off, len }
} else {
let old_buf = bytes.data()
if old_len <= old_buf.length() / 2 {
let new_buf = bytes.to_bytes()
SmallBytes::{ buf: Some(new_buf), off: 0, len: old_len.to_uint64() }
} else {
SmallBytes::{
buf: Some(old_buf),
off: bytes.start_offset().to_uint64(),
len: old_len.to_uint64(),
}
}
}
}
///|
pub fn SmallBytes::length(self : SmallBytes) -> Int {
match self.buf {
None => ((self.len & 0xFF00000000000000) >> 56).to_int()
Some(_) => self.len.to_int()
}
}
///|
pub fn SmallBytes::op_as_view(
self : SmallBytes,
start? : Int = 0,
end? : Int,
) -> SmallBytes {
match self.buf {
None => {
let old_len = self.len
let sem_len = ((old_len & 0xFF00000000000000) >> 56).to_int()
let end = match end {
None => sem_len
Some(e) => e
}
guard start >= 0 && end >= start && end <= sem_len else {
abort("SmallBytes::op_as_view: invalid range")
}
if start < 8 {
let bits = start * 8
let mask = (1UL << bits) - 1
let new_off = ((old_len & mask) << (64 - bits)) | (self.off >> bits)
let new_len = ((old_len & 0x00FFFFFFFFFFFFFF) >> bits) |
((end - start).to_uint64() << 56)
SmallBytes::{ buf: None, off: new_off, len: new_len }
} else {
let bits = (start - 8) * 8
let new_off = (old_len & 0x00FFFFFFFFFFFFFF) >> bits
let new_len = (end - start).to_uint64() << 56
SmallBytes::{ buf: None, off: new_off, len: new_len }
}
}
Some(buf) => {
let old_len = self.len.to_int()
let end = match end {
None => old_len
Some(e) => e
}
guard start >= 0 && end >= start && end <= old_len else {
abort("SmallBytes::op_as_view: invalid range")
}
let sem_len = end - start
if sem_len < 8 {
let new_off = for i = 0, off = 0UL; i < sem_len; {
let b = buf.unsafe_get(self.off.to_int() + start + i)
continue i + 1, off | (b.to_uint64() << (i * 8))
} else {
off
}
let new_len = sem_len.to_uint64() << 56
SmallBytes::{ buf: None, off: new_off, len: new_len }
} else if sem_len < 16 {
let new_off = buf.unsafe_get(self.off.to_int() + start).to_uint64() |
(buf.unsafe_get(self.off.to_int() + start + 1).to_uint64() << 8) |
(buf.unsafe_get(self.off.to_int() + start + 2).to_uint64() << 16) |
(buf.unsafe_get(self.off.to_int() + start + 3).to_uint64() << 24) |
(buf.unsafe_get(self.off.to_int() + start + 4).to_uint64() << 32) |
(buf.unsafe_get(self.off.to_int() + start + 5).to_uint64() << 40) |
(buf.unsafe_get(self.off.to_int() + start + 6).to_uint64() << 48) |
(buf.unsafe_get(self.off.to_int() + start + 7).to_uint64() << 56)
let new_len = for i = 8, new_len = 0UL; i < sem_len; {
let b = buf.unsafe_get(self.off.to_int() + start + i)
continue i + 1, new_len | (b.to_uint64() << ((i - 8) * 8))
} else {
new_len
}
let new_len = (sem_len.to_uint64() << 56) | new_len
SmallBytes::{ buf: None, off: new_off, len: new_len }
} else {
SmallBytes::{
buf: Some(buf),
off: self.off + start.to_uint64(),
len: sem_len.to_uint64(),
}
}
}
}
}
///|
pub fn SmallBytes::op_get(self : SmallBytes, index : Int) -> Byte {
guard index >= 0 && index < self.length() else {
abort("SmallBytes::op_get: index out of bounds")
}
match self.buf {
None =>
if index < 8 {
((self.off >> (index * 8)) & 0xFF).to_byte()
} else {
((self.len >> ((index - 8) * 8)) & 0xFF).to_byte()
}
Some(buf) => buf.unsafe_get(self.off.to_int() + index)
}
}