pub(super) fn gawk_order(keys: Vec<String>) -> Vec<String> {
let Some(first) = keys.first() else {
return keys;
};
match int_key(first) {
Some(n) if n >= 0 => cint_order(keys),
Some(_) => {
let mut a = IntArray::default();
for k in keys {
a.insert(k);
}
a.list()
}
None => {
let mut a = StrArray::default();
for k in keys {
a.insert(k);
}
a.list()
}
}
}
fn int_key(k: &str) -> Option<i64> {
let digits = k.strip_prefix('-').unwrap_or(k);
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
if digits.len() > 1 && digits.starts_with('0') || k == "-0" {
return None;
}
let n: i64 = k.parse().ok()?;
(i64::from(i32::MIN)..=i64::from(i32::MAX))
.contains(&n)
.then_some(n)
}
const SIZES: [usize; 21] = [
13, 127, 1021, 8191, 16381, 32749, 65497, 131101, 262147, 524309, 1048583, 2097169, 4194319,
8388617, 16777259, 33554467, 67108879, 134217757, 268435459, 536870923, 1073741827,
];
const CHAIN_MAX: usize = 2;
fn next_size(cur: usize) -> Option<usize> {
SIZES.iter().copied().find(|&s| s > cur)
}
fn str_hash(k: &str) -> u64 {
let mut h: u64 = 0;
for &b in k.as_bytes() {
let c = b as i8 as i64 as u64;
let t = h << 6;
h = c.wrapping_add(t).wrapping_add(t << 10).wrapping_sub(h) & 0xFFFF_FFFF;
}
h
}
#[derive(Default)]
struct StrArray {
buckets: Vec<Vec<(u64, String)>>,
len: usize,
}
impl StrArray {
fn insert(&mut self, k: String) {
if self.buckets.is_empty() {
self.buckets = vec![Vec::new(); SIZES[0]];
}
let code = str_hash(&k);
self.len += 1;
if self.len / self.buckets.len() > CHAIN_MAX
&& let Some(n) = next_size(self.buckets.len())
{
let old = std::mem::replace(&mut self.buckets, vec![Vec::new(); n]);
for (c, key) in old.into_iter().flatten() {
self.buckets[(c % n as u64) as usize].insert(0, (c, key));
}
}
let n = self.buckets.len() as u64;
self.buckets[(code % n) as usize].insert(0, (code, k));
}
fn list(self) -> Vec<String> {
self.buckets.into_iter().flatten().map(|(_, k)| k).collect()
}
}
fn int_hash(k: i64, size: usize) -> usize {
let mut k = k as u32;
k ^= k << 3;
k = k.wrapping_add(k >> 5);
k ^= k << 4;
k = k.wrapping_add(k >> 17);
k ^= k << 25;
k = k.wrapping_add(k >> 6);
(k as usize) % size
}
#[derive(Default)]
struct IntArray {
buckets: Vec<Vec<Vec<(i64, String)>>>,
ints: usize,
rest: Option<StrArray>,
}
impl IntArray {
fn insert(&mut self, k: String) {
let Some(n) = int_key(&k) else {
self.rest.get_or_insert_with(StrArray::default).insert(k);
return;
};
if self.buckets.is_empty() {
self.buckets = vec![Vec::new(); SIZES[0]];
}
self.ints += 1;
if self.ints / self.buckets.len() > CHAIN_MAX
&& let Some(size) = next_size(self.buckets.len())
{
let old = std::mem::replace(&mut self.buckets, vec![Vec::new(); size]);
for (num, key) in old.into_iter().flatten().flatten() {
self.put(num, key);
}
}
self.put(n, k);
}
fn put(&mut self, n: i64, k: String) {
let size = self.buckets.len();
let chain = &mut self.buckets[int_hash(n, size)];
match chain.first_mut() {
Some(head) if head.len() < 2 => head.push((n, k)),
_ => chain.insert(0, vec![(n, k)]),
}
}
fn list(self) -> Vec<String> {
let mut out = self.rest.map(StrArray::list).unwrap_or_default();
out.extend(self.buckets.into_iter().flatten().flatten().map(|(_, k)| k));
out
}
}
const NHAT: u32 = 10;
const THRESHOLD: i64 = 1 << (NHAT + 1);
fn cint_group(k: i64) -> u32 {
let r = 63 - (k.max(1) as u64).leading_zeros();
if r < NHAT { NHAT } else { r + 1 }
}
fn cint_leaf(k: i64) -> (i64, i64) {
let m = cint_group(k) - 1;
let (mut m, mut base) = if m < NHAT { (NHAT, 0) } else { (m, 1i64 << m) };
loop {
let n = m.div_ceil(2);
let size = 1i64 << n;
base += size * ((k - base) / size);
if n > NHAT {
m = n;
} else {
return (base, size);
}
}
}
fn cint_order(keys: Vec<String>) -> Vec<String> {
let mut leaves = std::collections::HashSet::new();
let mut capacity: i64 = 0;
let mut ints: Vec<(i64, String)> = Vec::new();
let mut rest: Option<Rest> = None;
for k in keys {
if let Some(n) = int_key(&k).filter(|&n| n >= 0) {
let m = cint_group(n) - 1;
let mut li = m.max(NHAT);
while li >= NHAT {
li = li.div_ceil(2);
}
if capacity + (1i64 << li) - ints.len() as i64 <= THRESHOLD {
let (base, size) = cint_leaf(n);
if leaves.insert(base) {
capacity += size;
}
ints.push((n, k));
continue;
}
}
match &mut rest {
Some(Rest::Int(a)) => a.insert(k),
Some(Rest::Str(a)) => a.insert(k),
None if int_key(&k).is_some() => {
let mut a = IntArray::default();
a.insert(k);
rest = Some(Rest::Int(a));
}
None => {
let mut a = StrArray::default();
a.insert(k);
rest = Some(Rest::Str(a));
}
}
}
ints.sort_by_key(|(n, _)| *n);
let mut out = match rest {
Some(Rest::Int(a)) => a.list(),
Some(Rest::Str(a)) => a.list(),
None => Vec::new(),
};
out.extend(ints.into_iter().map(|(_, k)| k));
out
}
enum Rest {
Int(IntArray),
Str(StrArray),
}
#[cfg(test)]
mod tests {
use super::*;
fn order(keys: &[&str]) -> Vec<String> {
gawk_order(keys.iter().map(|k| k.to_string()).collect())
}
#[test]
fn string_keys_follow_hash_buckets() {
assert_eq!(
order(&["engineering", "marketing", "sales", "support"]),
["marketing", "support", "sales", "engineering"]
);
}
#[test]
fn integer_keys_ascend() {
assert_eq!(order(&["3", "1", "10", "2"]), ["1", "2", "3", "10"]);
}
#[test]
fn non_integer_keys_list_before_integers() {
let got = order(&["2", "x", "1"]);
assert_eq!(got, ["x", "1", "2"]);
}
#[test]
fn int_key_is_canonical_i32() {
assert_eq!(int_key("0"), Some(0));
assert_eq!(int_key("-3"), Some(-3));
assert_eq!(int_key("03"), None);
assert_eq!(int_key("-0"), None);
assert_eq!(int_key("+3"), None);
assert_eq!(int_key("1.5"), None);
assert_eq!(int_key("2147483648"), None);
assert_eq!(int_key(""), None);
}
#[test]
fn every_key_listed_once() {
let keys: Vec<String> = (0..5000).map(|i| format!("k{i}")).collect();
let mut got = gawk_order(keys.clone());
assert_eq!(got.len(), keys.len());
got.sort();
let mut want = keys;
want.sort();
assert_eq!(got, want);
let ints: Vec<String> = (-3000..3000).map(|i| i.to_string()).collect();
assert_eq!(gawk_order(ints.clone()).len(), ints.len());
let sparse: Vec<String> = (0..200).map(|i| (i * 100_000).to_string()).collect();
assert_eq!(gawk_order(sparse).len(), 200);
}
}