///|
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())
}
///|
// Up to fifteen decimal digits remain exact throughout these Double operations.
// Fractions and longer integers use the exact decimal comparator.
fn integral_prefix(key : Bytes) -> Double? {
let mut i = 0
while i < key.length() && byte_is_blank(key[i]) {
i += 1
}
let negative = i < key.length() && key[i] == b'-'
if negative {
i += 1
}
let start = i
let mut value = 0.0
while i < key.length() && byte_is_digit(key[i]) {
if i - start == 15 {
return None
}
value = value * 10.0 + (key[i].to_int() - 48).to_double()
i += 1
}
if i < key.length() && key[i] == b'.' {
return None
}
Some(if negative { -value } else { value })
}
///|
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,
cached_numeric~ : Bool = false,
) -> Int32 {
let result = match modifiers.mode {
Lexical => left.lexical_compare(right)
Numeric =>
if cached_numeric {
numeric_field_value(left).compare(numeric_field_value(right))
} else {
numeric_compare(left, right)
}
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,
cached_numeric=true,
)
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 {
let ranges = field_ranges(record, separator)
specs.map(spec => {
let selected = extract_key(record, spec, ranges)
if spec.modifiers.mode == Numeric && !transforms_key(spec.modifiers) {
return prepare_numeric_field(selected)
}
let key = prepared_key(selected, spec.modifiers, seed)
if spec.modifiers.mode == Numeric {
prepare_numeric_field(key)
} else {
key
}
})
}
{ record, keys, index, }
}
///|
// Select ordinary byte ordering once per invocation. Numeric/key policies stay
// in the general comparator; stable sequence order still resolves equal bytes.
fn entry_comparator(
specs : Array[KeySpec],
global : Modifiers,
last_resort : Bool,
) -> (Entry, Entry) -> Int32 {
if specs.is_empty() && global.mode == Lexical && !transforms_key(global) {
let reverse = global.reverse
(left, right) => {
let order = left.record.lexical_compare(right.record)
if order == 0 {
left.index.compare(right.index)
} else if reverse {
-order
} else {
order
}
}
} else {
(left, right) => {
let order = compare_entries(left, right, specs, global, last_resort)
if order == 0 {
left.index.compare(right.index)
} else {
order
}
}
}
}