///|
fn numeric_slice(key : Bytes, exponent : Bool, allow_plus : Bool) -> Bytes? {
let mut index = 0
while index < key.length() && byte_is_blank(key[index]) {
index += 1
}
let start = index
if index < key.length() && key[index] is b'-' {
index += 1
} else if allow_plus && index < key.length() && key[index] is b'+' {
index += 1
}
let mut digits = 0
while index < key.length() && byte_is_digit(key[index]) {
digits += 1
index += 1
}
if index < key.length() && key[index] is b'.' {
index += 1
while index < key.length() && byte_is_digit(key[index]) {
digits += 1
index += 1
}
}
if exponent &&
digits > 0 &&
index < key.length() &&
(key[index] is b'e' || key[index] is b'E') {
let exponent_start = index
index += 1
if index < key.length() && (key[index] is b'+' || key[index] is b'-') {
index += 1
}
let exponent_digits = index
while index < key.length() && byte_is_digit(key[index]) {
index += 1
}
if exponent_digits == index {
index = exponent_start
}
}
if digits == 0 {
return None
}
Some(key[start:index].to_owned())
}
///|
fn numeric_prefix(key : Bytes, exponent : Bool) -> Double {
match numeric_slice(key, exponent, exponent) {
Some(number) =>
@string.parse_double(@utf8.decode(number)) catch {
_ => 0.0
}
None => 0.0
}
}
///|
fn ascii_lower_byte(byte : Byte) -> Byte {
let value = byte.to_int()
if value >= 0x41 && value <= 0x5A {
(value + 0x20).to_byte()
} else {
byte
}
}
///|
fn ascii_prefix_at(key : Bytes, index : Int32, prefix : Bytes) -> Bool {
if index + prefix.length() > key.length() {
return false
}
for offset, byte in prefix {
if ascii_lower_byte(key[index + offset]) != byte {
return false
}
}
true
}
///|
fn general_number(key : Bytes) -> GeneralNumber {
let mut index = 0
while index < key.length() && byte_is_blank(key[index]) {
index += 1
}
let mut negative = false
if index < key.length() && key[index] is b'-' {
negative = true
index += 1
} else if index < key.length() && key[index] is b'+' {
index += 1
}
if ascii_prefix_at(key, index, b"nan") {
return NotANumber
}
if ascii_prefix_at(key, index, b"inf") {
return if negative { NegativeInfinity } else { PositiveInfinity }
}
match numeric_slice(key, true, true) {
None => NonNumber
Some(number) => {
let value = @string.parse_double(@utf8.decode(number)) catch {
_ => return NonNumber
}
if value.is_nan() {
NotANumber
} else if value.is_inf() {
if value < 0.0 {
NegativeInfinity
} else {
PositiveInfinity
}
} else {
Finite(value)
}
}
}
}
///|
fn general_compare(left : Bytes, right : Bytes) -> Int32 {
match (general_number(left), general_number(right)) {
(NonNumber, NonNumber) | (NotANumber, NotANumber) => 0
(NonNumber, _) => -1
(_, NonNumber) => 1
(NotANumber, _) => -1
(_, NotANumber) => 1
(NegativeInfinity, NegativeInfinity) => 0
(NegativeInfinity, _) => -1
(_, NegativeInfinity) => 1
(PositiveInfinity, PositiveInfinity) => 0
(PositiveInfinity, _) => 1
(_, PositiveInfinity) => -1
(Finite(left), Finite(right)) => left.compare(right)
}
}
///|
fn human_parts(key : Bytes) -> (Int32, Int32, Double) {
let mut index = 0
while index < key.length() && byte_is_blank(key[index]) {
index += 1
}
let start = index
if index < key.length() && key[index] is b'-' {
index += 1
} else if index < key.length() && key[index] is b'+' {
return (0, 0, 0.0)
}
let mut digits = 0
while index < key.length() && byte_is_digit(key[index]) {
digits += 1
index += 1
}
if index < key.length() && key[index] is b'.' {
index += 1
while index < key.length() && byte_is_digit(key[index]) {
digits += 1
index += 1
}
}
if digits == 0 {
return (0, 0, 0.0)
}
let number = @string.parse_double(@utf8.decode(key[start:index].to_owned())) catch {
_ => 0.0
}
let power = if index < key.length() {
match key[index] {
b'K' | b'k' => 1
b'M' => 2
b'G' => 3
b'T' => 4
b'P' => 5
b'E' => 6
b'Z' => 7
b'Y' => 8
b'R' => 9
b'Q' => 10
_ => 0
}
} else {
0
}
let sign = if number < 0.0 { -1 } else if number > 0.0 { 1 } else { 0 }
(sign, power, number)
}
///|
fn human_compare(left : Bytes, right : Bytes) -> Int32 {
let (left_sign, left_power, left_value) = human_parts(left)
let (right_sign, right_power, right_value) = human_parts(right)
if left_sign != right_sign {
return left_sign - right_sign
}
if left_power != right_power {
return if left_sign < 0 {
right_power - left_power
} else {
left_power - right_power
}
}
left_value.compare(right_value)
}
///|
fn month_value(key : Bytes) -> Int32 {
let text = transformed_key(key, {
ignore_leading_blanks: true,
dictionary_order: false,
fold_case: true,
ignore_nonprinting: false,
mode: Lexical,
ordering_modes: 0,
reverse: false,
})
let text = @utf8.decode(text) catch { _ => "" }
if text.length() < 3 {
return 0
}
match text[0:3].to_owned() {
"JAN" => 1
"FEB" => 2
"MAR" => 3
"APR" => 4
"MAY" => 5
"JUN" => 6
"JUL" => 7
"AUG" => 8
"SEP" => 9
"OCT" => 10
"NOV" => 11
"DEC" => 12
_ => 0
}
}
///|
fn version_compare(left : Bytes, right : Bytes) -> Int32 {
let mut li = 0
let mut ri = 0
while li < left.length() || ri < right.length() {
if li < left.length() && left[li] is b'~' {
if ri < right.length() && right[ri] is b'~' {
li += 1
ri += 1
continue
}
return -1
}
if ri < right.length() && right[ri] is b'~' {
return 1
}
if li == left.length() {
return -1
}
if ri == right.length() {
return 1
}
if byte_is_digit(left[li]) && byte_is_digit(right[ri]) {
let mut lzero = li
while lzero < left.length() && left[lzero] is b'0' {
lzero += 1
}
let mut rzero = ri
while rzero < right.length() && right[rzero] is b'0' {
rzero += 1
}
let mut lend = lzero
while lend < left.length() && byte_is_digit(left[lend]) {
lend += 1
}
let mut rend = rzero
while rend < right.length() && byte_is_digit(right[rend]) {
rend += 1
}
let llen = lend - lzero
let rlen = rend - rzero
if llen != rlen {
return llen - rlen
}
let cmp = left[lzero:lend].lexical_compare(right[rzero:rend])
if cmp != 0 {
return cmp
}
li = lend
ri = rend
} else {
if left[li] != right[ri] {
return left[li].to_int() - right[ri].to_int()
}
li += 1
ri += 1
}
}
0
}
///|
fn compare_prepared_key(
left : Bytes,
right : Bytes,
modifiers : Modifiers,
) -> Int32 {
let result = match modifiers.mode {
Lexical => left.lexical_compare(right)
Numeric => numeric_prefix(left, false).compare(numeric_prefix(right, false))
GeneralNumeric => general_compare(left, right)
HumanNumeric => human_compare(left, right)
Month => month_value(left) - month_value(right)
Version => version_compare(left, right)
Random => left.lexical_compare(right)
}
if modifiers.reverse {
-result
} else {
result
}
}
///|
fn compare_entries(
left : Entry,
right : Entry,
specs : Array[KeySpec],
global : Modifiers,
last_resort : Bool,
) -> Int32 {
if specs.is_empty() {
let result = compare_prepared_key(
left.keys.get(0).unwrap_or(left.record),
right.keys.get(0).unwrap_or(right.record),
global,
)
if result != 0 || !last_resort {
return result
}
} else {
for index, spec in specs {
let result = compare_prepared_key(
left.keys[index],
right.keys[index],
spec.modifiers,
)
if result != 0 {
return result
}
}
if !last_resort {
return 0
}
}
let result = left.record.lexical_compare(right.record)
if global.reverse {
-result
} else {
result
}
}
///|
fn make_entry(
record : Bytes,
index : Int64,
specs : Array[KeySpec],
global : Modifiers,
separator : Byte?,
seed~ : Bytes = b"",
) -> Entry {
let keys = if specs.is_empty() {
if transforms_key(global) || global.mode == Random {
[prepared_key(record, global, seed)]
} else {
[]
}
} else {
specs.map(spec => {
prepared_key(extract_key(record, spec, separator), spec.modifiers, seed)
})
}
{ record, keys, index, }
}