diff --git a/core/rs/fractindex-core/src/fractindex.rs b/core/rs/fractindex-core/src/fractindex.rs index a354cf53d..f46f8c1f3 100644 --- a/core/rs/fractindex-core/src/fractindex.rs +++ b/core/rs/fractindex-core/src/fractindex.rs @@ -5,21 +5,21 @@ use alloc::{ string::{String, ToString}, }; -pub static BASE_62_DIGITS: &'static str = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +pub static BASE_95_DIGITS: &'static str = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; -static SMALLEST_INTEGER: &'static str = "A00000000000000000000000000"; -static INTEGER_ZERO: &'static str = "a0"; +static SMALLEST_INTEGER: &'static str = "A "; +static INTEGER_ZERO: &'static str = "a "; static a_charcode: u8 = 97; static z_charcode: u8 = 122; static A_charcode: u8 = 65; static Z_charcode: u8 = 90; -static zero_charcode: u8 = 48; +static min_charcode: u8 = 32; pub fn key_between(a: Option<&str>, b: Option<&str>) -> Result, &'static str> { // configurable digits not yet supported - let digits = BASE_62_DIGITS; + let digits = BASE_95_DIGITS; a.map(|a| validate_order_key(a)).transpose()?; b.map(|b| validate_order_key(b)).transpose()?; @@ -91,10 +91,10 @@ fn midpoint(a: &str, b: Option<&str>, digits: &str) -> Result 0 && a_bytes[a_bytes.len() - 1] == zero_charcode - || (b_last_char.map_or(false, |b| b == zero_charcode)) + if a_bytes.len() > 0 && a_bytes[a_bytes.len() - 1] == min_charcode + || (b_last_char.map_or(false, |b| b == min_charcode)) { - return Err("midpoint - a or b must not end with 0"); + return Err("midpoint - a or b must not end with ' ' (space)"); } if let Some(b) = b { @@ -104,7 +104,7 @@ fn midpoint(a: &str, b: Option<&str>, digits: &str) -> Result Result<(), &'static str> { let i = get_integer_part(key)?; let f = &key[i.len()..]; let as_bytes = f.as_bytes(); - if as_bytes.len() > 0 && as_bytes[as_bytes.len() - 1] == zero_charcode { - return Err("Fractional part should not end with 0"); + if as_bytes.len() > 0 && as_bytes[as_bytes.len() - 1] == min_charcode { + return Err("Fractional part should not end with ' ' (space)"); } Ok(()) @@ -224,7 +224,7 @@ fn increment_integer(x: &str, digits: &str) -> Result, &'static s let d = temp + 1; if d == digits.len() { - digs.replace_range(ui..ui + 1, "0"); + digs.replace_range(ui..ui + 1, &digits[0..1]); } else { digs.replace_range(ui..ui + 1, &digits[d..d + 1]); carry = false; @@ -237,14 +237,14 @@ fn increment_integer(x: &str, digits: &str) -> Result, &'static s if carry { if head == "Z" { - return Ok(Some(String::from("a0"))); + return Ok(Some(String::from(INTEGER_ZERO))); } if head == "z" { return Ok(None); } let h = head.as_bytes()[0] + 1; if h > a_charcode { - digs.push('0'); + digs.push(digits.chars().nth(0).unwrap()); } else { digs.pop(); } @@ -333,34 +333,34 @@ mod tests { assert_eq!(btwn, exp); } - test(None, None, Ok(Some(String::from("a0")))); - test(None, Some("a0"), Ok(Some(String::from("Zz")))); - test(None, Some("Zz"), Ok(Some(String::from("Zy")))); - test(Some("a0"), None, Ok(Some(String::from("a1")))); - test(Some("a1"), None, Ok(Some(String::from("a2")))); - test(Some("a0"), Some("a1"), Ok(Some(String::from("a0V")))); - test(Some("a1"), Some("a2"), Ok(Some(String::from("a1V")))); - test(Some("a0V"), Some("a1"), Ok(Some(String::from("a0l")))); - test(Some("Zz"), Some("a0"), Ok(Some(String::from("ZzV")))); - test(Some("Zz"), Some("a1"), Ok(Some(String::from("a0")))); - test(None, Some("Y00"), Ok(Some(String::from("Xzzz")))); - test(Some("bzz"), None, Ok(Some(String::from("c000")))); - test(Some("a0"), Some("a0V"), Ok(Some(String::from("a0G")))); - test(Some("a0"), Some("a0G"), Ok(Some(String::from("a08")))); + test(None, None, Ok(Some(String::from("a ")))); + test(None, Some("a "), Ok(Some(String::from("Z~")))); + test(None, Some("Z~"), Ok(Some(String::from("Z}")))); + test(Some("a "), None, Ok(Some(String::from("a!")))); + test(Some("a!"), None, Ok(Some(String::from("a\"")))); + test(Some("a0"), Some("a1"), Ok(Some(String::from("a0P")))); + test(Some("a1"), Some("a2"), Ok(Some(String::from("a1P")))); + test(Some("a0V"), Some("a1"), Ok(Some(String::from("a0k")))); + test(Some("Z~"), Some("a "), Ok(Some(String::from("Z~P")))); + test(Some("Z~"), Some("a!"), Ok(Some(String::from("a ")))); + test(None, Some("Y "), Ok(Some(String::from("X~~~")))); + test(Some("b~~"), None, Ok(Some(String::from("c ")))); + test(Some("a0"), Some("a0V"), Ok(Some(String::from("a0;")))); + test(Some("a0"), Some("a0G"), Ok(Some(String::from("a04")))); test(Some("b125"), Some("b129"), Ok(Some(String::from("b127")))); test(Some("a0"), Some("a1V"), Ok(Some(String::from("a1")))); - test(Some("Zz"), Some("a01"), Ok(Some(String::from("a0")))); + test(Some("Z~"), Some("a 1"), Ok(Some(String::from("a ")))); test(None, Some("a0V"), Ok(Some(String::from("a0")))); test(None, Some("b999"), Ok(Some(String::from("b99")))); test( None, - Some("A00000000000000000000000000"), + Some("A "), Err("Key is too small"), ); test( None, - Some("A000000000000000000000000001"), - Ok(Some(String::from("A000000000000000000000000000V"))), + Some("A !"), + Ok(Some(String::from("A P"))), ); test( Some("zzzzzzzzzzzzzzzzzzzzzzzzzzy"), @@ -368,19 +368,19 @@ mod tests { Ok(Some(String::from("zzzzzzzzzzzzzzzzzzzzzzzzzzz"))), ); test( - Some("zzzzzzzzzzzzzzzzzzzzzzzzzzz"), + Some("z~~~~~~~~~~~~~~~~~~~~~~~~~~"), None, - Ok(Some(String::from("zzzzzzzzzzzzzzzzzzzzzzzzzzzV"))), + Ok(Some(String::from("z~~~~~~~~~~~~~~~~~~~~~~~~~~P"))), ); test( - Some("a00"), + Some("a0 "), None, - Err("Fractional part should not end with 0"), + Err("Fractional part should not end with ' ' (space)"), ); test( - Some("a00"), + Some("a0 "), Some("a1"), - Err("Fractional part should not end with 0"), + Err("Fractional part should not end with ' ' (space)"), ); test(Some("0"), Some("1"), Err("head is out of range")); test( diff --git a/core/src/rs-fract.test.c b/core/src/rs-fract.test.c index 0d9d1e47c..432276b83 100644 --- a/core/src/rs-fract.test.c +++ b/core/src/rs-fract.test.c @@ -43,7 +43,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); const unsigned char *order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a0") == 0); + assert(strcmp((const char *)order, "a ") == 0); sqlite3_finalize(pStmt); // test append @@ -55,7 +55,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a1") == 0); + assert(strcmp((const char *)order, "a!") == 0); sqlite3_finalize(pStmt); // test insert after head @@ -71,7 +71,7 @@ static void testAsOrdered() { while (sqlite3_step(pStmt) == SQLITE_ROW) { assert(sqlite3_column_int(pStmt, 0) == i); if (i == 2) { - assert(strcmp((const char *)sqlite3_column_text(pStmt, 1), "a0V") == 0); + assert(strcmp((const char *)sqlite3_column_text(pStmt, 1), "a P") == 0); } i += 1; } @@ -88,7 +88,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "Zz") == 0); + assert(strcmp((const char *)order, "Z~") == 0); sqlite3_finalize(pStmt); // append to a list with items via 1 trick @@ -102,7 +102,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a2") == 0); + assert(strcmp((const char *)order, "a\"") == 0); sqlite3_finalize(pStmt); // before head via view and null @@ -116,7 +116,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "Zy") == 0); + assert(strcmp((const char *)order, "Z}") == 0); sqlite3_finalize(pStmt); // after tail view view @@ -130,7 +130,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a3") == 0); + assert(strcmp((const char *)order, "a#") == 0); sqlite3_finalize(pStmt); // test move after @@ -142,17 +142,17 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a2V") == 0); + assert(strcmp((const char *)order, "a\"P") == 0); sqlite3_finalize(pStmt); /* - -1 -> Zy - 0 -> Zz - 1 -> a0 + -1 -> Z} + 0 -> Z~ + 1 -> a 2 -> ? - 4 -> a2 - 3 -> a2V - 5 -> a3 + 4 -> a" + 3 -> a"P + 5 -> a# */ // insert between / insert after @@ -166,17 +166,17 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a0V") == 0); + assert(strcmp((const char *)order, "a P") == 0); sqlite3_finalize(pStmt); /* - -1 -> Zy - 0 -> Zz - 1 -> a0 - 2 -> a0V - 4 -> a2 - 3 -> a2V - 5 -> a3 + -1 -> Z} + 0 -> Z~ + 1 -> a + 2 -> a P + 4 -> a" + 3 -> a"P + 5 -> a# */ // move before @@ -189,24 +189,24 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a1") == 0); + assert(strcmp((const char *)order, "a!") == 0); sqlite3_finalize(pStmt); /* - -1 -> Zy - 0 -> Zz - 1 -> a0 - 2 -> a0V - 3 -> a1 - 4 -> a2 - 5 -> a3 + -1 -> Z} + 0 -> Z~ + 1 -> a + 2 -> a P + 3 -> a! + 4 -> a" + 5 -> a# */ // make some collisions rc = sqlite3_exec( db, "INSERT INTO todo (id, list_id, content, complete, ordering) " - "VALUES (6, 1, 'xx', false, 'a1')", + "VALUES (6, 1, 'xx', false, 'a!')", 0, 0, 0); assert(rc == SQLITE_OK); // 3 & 6 collide, try insertion after 3 @@ -222,7 +222,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a0t") == 0); + assert(strcmp((const char *)order, "a t") == 0); sqlite3_finalize(pStmt); rc += sqlite3_prepare_v2(db, "SELECT ordering FROM todo WHERE id = 3", -1, @@ -230,7 +230,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a0l") == 0); + assert(strcmp((const char *)order, "a h") == 0); sqlite3_finalize(pStmt); rc += sqlite3_prepare_v2(db, "SELECT ordering FROM todo WHERE id = 6", -1, @@ -238,7 +238,7 @@ static void testAsOrdered() { assert(rc == SQLITE_OK); sqlite3_step(pStmt); order = sqlite3_column_text(pStmt, 0); - assert(strcmp((const char *)order, "a1") == 0); + assert(strcmp((const char *)order, "a!") == 0); sqlite3_finalize(pStmt); // Test many list column diff --git a/py/correctness/tests/test_as_ordered.py b/py/correctness/tests/test_as_ordered.py index bb2b0c7c6..6b9184636 100644 --- a/py/correctness/tests/test_as_ordered.py +++ b/py/correctness/tests/test_as_ordered.py @@ -19,7 +19,7 @@ def test_first_insertion_prepend(): c.commit() rows = c.execute("SELECT * FROM foo").fetchall() - assert (rows == [(1, 'a0', 'a')]) + assert (rows == [(1, 'a ', 'a')]) def test_first_insertion_append(): @@ -28,7 +28,7 @@ def test_first_insertion_append(): c.commit() rows = c.execute("SELECT * FROM foo").fetchall() - assert (rows == [(1, 'a0', 'a')]) + assert (rows == [(1, 'a ', 'a')]) def test_middle_insertion(): @@ -39,7 +39,7 @@ def test_middle_insertion(): c.commit() rows = c.execute("SELECT * FROM foo ORDER BY spot ASC").fetchall() - assert (rows == [(1, 'a0', 'list'), (2, 'a0V', 'list'), (3, 'a1', 'list')]) + assert (rows == [(1, 'a ', 'list'), (2, 'a P', 'list'), (3, 'a!', 'list')]) def test_front_insertion(): @@ -51,7 +51,7 @@ def test_front_insertion(): c.commit() rows = c.execute("SELECT * FROM foo ORDER BY spot ASC").fetchall() - assert ([(1, 'Zz', 'list'), (2, 'a0', 'list'), (3, 'a1', 'list')] == rows) + assert ([(1, 'Z~', 'list'), (2, 'a ', 'list'), (3, 'a!', 'list')] == rows) def test_endinsertion(): @@ -63,7 +63,7 @@ def test_endinsertion(): c.commit() rows = c.execute("SELECT * FROM foo ORDER BY spot ASC").fetchall() - assert (rows == [(1, 'a0', 'list'), (2, 'a1', 'list'), (3, 'a2', 'list')]) + assert (rows == [(1, 'a ', 'list'), (2, 'a!', 'list'), (3, 'a"', 'list')]) def test_view_first_insertion(): @@ -73,7 +73,7 @@ def test_view_first_insertion(): c.commit() rows = c.execute("SELECT * FROM foo").fetchall() - assert (rows == [(1, 'a0', 'list')]) + assert (rows == [(1, 'a ', 'list')]) def test_view_move(): @@ -87,4 +87,4 @@ def test_view_move(): c.execute("UPDATE foo_fractindex SET after_a = 1 WHERE a = 3") c.commit() rows = c.execute("SELECT * FROM foo ORDER BY spot ASC").fetchall() - assert (rows == [(1, 'a0', 'list'), (3, 'a0V', 'list'), (2, 'a1', 'list')]) + assert (rows == [(1, 'a ', 'list'), (3, 'a P', 'list'), (2, 'a!', 'list')])