index
Avril 4 years ago
parent 2eefaef0ae
commit 5d46a7bf6b
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1,112 @@
//! Entry API
use super::*;
#[derive(Debug)]
pub struct OccupiedEntry<'a, K, V>(pub(crate) &'a mut Option<(K,V)>);
impl<'a, K, V> OccupiedEntry<'a, K, V>
where K: Collapse
{
pub fn get(&self) -> &V
{
&self.0.as_ref().unwrap().1
}
pub fn get_mut(&mut self) -> &mut V
{
&mut self.0.as_mut().unwrap().1
}
pub fn into_mut(self) -> &'a mut V
{
&mut self.0.as_mut().unwrap().1
}
pub fn key(&self) -> &K
{
&self.0.as_ref().unwrap().0
}
pub fn insert(&mut self, value: V) -> V
{
std::mem::replace(&mut self.0.as_mut().unwrap().1, value)
}
pub fn remove(self) -> V
{
self.remove_entry().1
}
pub fn remove_entry(self) -> (K, V)
{
self.0.take().unwrap()
}
}
#[derive(Debug)]
pub struct VacantEntry<'a,K,V>(pub(crate) &'a mut Option<(K,V)>, pub(crate) K);
impl<'a, K, V> VacantEntry<'a, K, V>
where K: Collapse
{
pub fn insert(self, value: V) -> &'a mut V
{
*self.0 = Some((self.1, value));
&mut self.0.as_mut().unwrap().1
}
pub fn into_key(self) -> K
{
self.1
}
pub fn key(&self) -> &K
{
&self.1
}
}
#[derive(Debug)]
pub enum Entry<'a, K, V>
{
Vacant(VacantEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>),
}
impl<'a, K, V> Entry<'a, K, V>
where K: Collapse
{
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Entry<'a, K, V>
{
if let Self::Occupied(occuped) = &mut self {
f(occuped.get_mut())
}
self
}
pub fn key(&self) -> &K
{
match self {
Entry::Vacant(v) => v.key(),
Entry::Occupied(o) => o.key(),
}
}
pub fn or_insert_with<F: FnOnce() -> V>(self, with: F) -> &'a mut V
{
match self {
Entry::Occupied(o) => o.into_mut(),
Entry::Vacant(v) => v.insert(with())
}
}
#[inline] pub fn or_insert(self, value: V) -> &'a mut V
{
self.or_insert_with(|| value)
}
}
impl<'a, K, V> Entry<'a, K, V>
where K: Collapse,
V: Default
{
#[inline] pub fn or_default(self) -> &'a mut V
{
self.or_insert_with(Default::default)
}
}

@ -94,7 +94,7 @@ impl<K, V> std::iter::FusedIterator for IntoPageElements<K,V>{}
pub struct Iter<'a, K, V>(pub(crate) Option<PageElements<'a,K,V>>, pub(crate) Pages<'a, K,V>);
impl<'a, K,V> Iterator for Iter<'a, K,V>
where K: Collapsible
where K: Collapse
{
type Item = &'a (K,V);
fn next(&mut self) -> Option<Self::Item> {
@ -115,13 +115,13 @@ where K: Collapsible
(0, self.1.size_hint().1.map(|x| x * MAX))
}
}
impl<'a, K: Collapsible, V> std::iter::FusedIterator for Iter<'a, K,V>{}
impl<'a, K: Collapse, V> std::iter::FusedIterator for Iter<'a, K,V>{}
pub struct IterMut<'a, K, V>(pub(crate) Option<PageElementsMut<'a,K,V>>, pub(crate) PagesMut<'a, K,V>);
impl<'a, K,V> Iterator for IterMut<'a, K,V>
where K: Collapsible
where K: Collapse
{
type Item = &'a mut (K,V);
fn next(&mut self) -> Option<Self::Item> {
@ -142,14 +142,14 @@ where K: Collapsible
(0, self.1.size_hint().1.map(|x| x * MAX))
}
}
impl<'a, K: Collapsible, V> std::iter::FusedIterator for IterMut<'a, K,V>{}
impl<'a, K: Collapse, V> std::iter::FusedIterator for IterMut<'a, K,V>{}
pub struct IntoIter<K, V>(pub(crate) Option<IntoPageElements<K,V>>, pub(crate) std::vec::IntoIter<Page<K,V>>);
impl<K, V> Iterator for IntoIter<K,V>
where K: Collapsible
where K: Collapse
{
type Item = (K,V);
fn next(&mut self) -> Option<Self::Item> {
@ -172,4 +172,4 @@ impl<K, V> Iterator for IntoIter<K,V>
}
}
impl<K: Collapsible, V> std::iter::FusedIterator for IntoIter<K,V>{}
impl<K: Collapse, V> std::iter::FusedIterator for IntoIter<K,V>{}

@ -1,5 +1,6 @@
#![feature(const_in_array_repeat_expressions)]
#![feature(const_fn)]
#![feature(drain_filter)]
#![cfg_attr(nightly, feature(test))]
@ -9,11 +10,17 @@
const MAX: usize = 256;
//TODO: Move test
//TODO: Document
//TODO: Readme
//TODO: LICENSE
//TODO: Publish and upload to githubxc
use std::{
borrow::Borrow,
};
pub trait Collapsible: Eq
pub trait Collapse: Eq
{
fn collapse(&self) -> u8;
}
@ -23,7 +30,7 @@ pub trait Collapsible: Eq
pub struct Page<TKey,TValue>([Option<(TKey, TValue)>; MAX]);
impl<K,V> Page<K,V>
where K: Collapsible
where K: Collapse
{
/// Create a new blank page
pub const fn new() -> Self
@ -47,12 +54,12 @@ where K: Collapsible
}
fn search<Q: ?Sized>(&self, key: &Q) -> &Option<(K,V)>
where Q: Collapsible
where Q: Collapse
{
&self.0[usize::from(key.collapse())]
}
fn search_mut<Q: ?Sized>(&mut self, key: &Q) -> &mut Option<(K,V)>
where Q: Collapsible
where Q: Collapse
{
&mut self.0[usize::from(key.collapse())]
}
@ -64,7 +71,7 @@ where K: Collapsible
}
impl<K,V> IntoIterator for Page<K,V>
where K: Collapsible
where K: Collapse
{
type Item= (K,V);
type IntoIter = IntoPageElements<K,V>;
@ -77,7 +84,7 @@ where K: Collapsible
impl<K,V> Default for Page<K,V>
where K: Collapsible
where K: Collapse
{
#[inline]
fn default() -> Self
@ -91,10 +98,50 @@ pub struct Map<TKey, TValue>(Vec<Page<TKey,TValue>>);
pub mod iter;
use iter::*;
pub mod entry;
pub use entry::Entry;
impl<K,V> Map<K,V>
where K: Collapsible
where K: Collapse
{
fn new_page(&mut self) -> &mut Page<K,V>
{
let len = self.0.len();
self.0.push(Page::new());
&mut self.0[len]
}
#[inline(always)] fn fuck_entry(&mut self, key: K) -> Option<Entry<'_, K, V>>
{
for page in self.0.iter_mut()
{
let re = page.search_mut(&key);
match re {
Some((ref ok, _)) if key.eq(ok.borrow()) => {
return Some(Entry::Occupied(entry::OccupiedEntry(re)));
},
None => {
return Some(Entry::Vacant(entry::VacantEntry(re, key)));
},
_ => (),
}
}
None
}
pub fn entry(&mut self, key: K) -> Entry<'_, K, V>
{
if self.0.iter()
.filter(|x| x.search(&key).is_none())
.count() == 0 {
self.new_page();
}//so dumb..... SO dumb
//will need to completely reimplement all entry::* shit to just have mut reference to Map and then usize indecies for location I guess. Fuck this
self.fuck_entry(key).unwrap()
}
pub fn clean(&mut self)
{
self.0.drain_filter(|x| x.len() <1);
}
pub fn len(&self) -> usize
{
self.pages().map(Page::len).sum()
@ -151,7 +198,7 @@ where K: Collapsible
pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
where K: Borrow<Q>,
Q: Collapsible + Eq
Q: Collapse + Eq
{
for page in self.0.iter_mut()
{
@ -167,14 +214,14 @@ where K: Collapsible
#[inline] pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where K: Borrow<Q>,
Q: Collapsible + Eq
Q: Collapse + Eq
{
self.get(key).is_some()
}
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Collapsible + Eq
Q: Collapse + Eq
{
for page in self.0.iter()
{
@ -187,11 +234,28 @@ where K: Collapsible
}
None
}
fn search_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut Option<(K,V)>>
where K: Borrow<Q>,
Q: Collapse + Eq
{
for page in self.0.iter_mut()
{
let se = page.search_mut(key);
match se {
Some((ref ok, _)) if key.eq(ok.borrow()) => {
return Some(se);
},
_ => (),
}
}
None
}
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where K: Borrow<Q>,
Q: Collapsible + Eq
Q: Collapse + Eq
{
for page in self.0.iter_mut()
{
@ -229,7 +293,7 @@ where K: Collapsible
}
}
impl<K: Collapsible, V> IntoIterator for Map<K,V>
impl<K: Collapse, V> IntoIterator for Map<K,V>
{
type Item= (K,V);
type IntoIter = IntoIter<K,V>;
@ -245,7 +309,7 @@ pub trait CollapseMemory: Eq
{
fn as_memory(&self) -> &[u8];
}
impl<T> Collapsible for T
impl<T> Collapse for T
where T: CollapseMemory
{
fn collapse(&self) -> u8 {
@ -267,466 +331,4 @@ pub fn collapse<T: AsRef<[u8]>>(bytes: T) -> u8
}
#[cfg(test)]
mod tests
{
use super::*;
#[cfg(nightly)] use test::{Bencher, black_box};
#[test]
fn it_works()
{
let mut map = Map::new();
map.insert('>', false);
map.insert('<', true);
map.insert('ł', true);
let clone = map.clone();
for (k, v) in clone.into_iter()
{
assert!(map.get(&k) == Some(&v))
}
}
#[test]
fn hmap_comp()
{
let mut small = Map::new();
let mut hash = HashMap::new();
let input = r#"Bludgeoning clsql and mariadb until they kind of work
There are no good mysql connectors for Common Lisp, whatever you decide to use, you're not going to have a good time. clsql is pretty much the best we've got, and since I had such a delightful experience configuring it myself, I hope to smooth the process of getting this shitware up and running with my own little blogpost here.
connecting to a database
Connecting to mysql with clsql looks something like this
(clsql:connect
'("host" "database" "username" "password" &optional "port") ;; connection-spec
:database-type :mysql) ;; Defaults to clsql:*default-database-type*
clsql builds ffi libraries the first time you try to connect to a database, and you can get some pretty arcane error messages if you don't have the right shared libraries installed already. These are (hopefully) packaged by your distro already, in Gentoo you want
$ sudo emerge --ask dev-db/mariadb-connector-c
in openSUSE it's
$ zypper in mariadb-connector-odbc
if you're on Debian or Ubuntu then God save your soul, etc.
Despite mariadb being a drop-in replacement for mysql, the clsql developers have made little to no effort to actually support it, to the point where mariadb's versioning system isn't even accounted for when loading the libraries, causing clsql to just error out for absolutely no reason. (actually, this shitware doesn't even 'support' modern versions on mysql because the version numbers have increased above what they check for, but they load fine as well)
We can modify the version checking script, to fix this, which you can find here: ~/quicklisp/dists/quicklisp/software/clsql-*-git/db-mysql/mysql-client-info.lisp (If your quicklisp is installed in its default directory you can paste this straight into a terminal or VIM or some such and it'll expand itself, which is kinda neat.)
If we add the top three lines just before the bottom two it'll stopping giving you stupid error messages when you try and connect.
((and (eql (schar *mysql-client-info* 0) #\1)
(eql (schar *mysql-client-info* 0) #\0))
(pushnew :mysql-client-v6 cl:*features*))
(t
(error "Unknown mysql client version '~A'." *mysql-client-info*)))))
charset issues
After (finally) connecting, when we first query the database we might get an error like this:
debugger invoked on a BABEL-ENCODINGS:INVALID-UTF8-STARTER-BYTE in thread
#<THREAD "main thread" RUNNING {B3E2151}>: Illegal :UTF-8 character starting at position 18.
We can identify the issue like so:
(defvar charsets
"SELECT VARIABLE_NAME, SESSION_VALUE \
FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES \
WHERE VARIABLE_NAME LIKE 'character_set_c%' \
OR VARIABLE_NAME LIKE 'character_set_re%' \
OR VARIABLE_NAME LIKE 'collation_c%';")
;; CHARSETS
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "latin1_swedish_ci")
;; ("CHARACTER_SET_CONNECTION" "latin1")
;; ("CHARACTER_SET_RESULTS" "latin1")
;; ("CHARACTER_SET_CLIENT" "latin1"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
These are actually the default collation and character sets in MYSQL; Smarter mysql drivers would set their charset and collation to utf8 by themseleves, but not clsql! (the collation is set to latin1_swedish_ci by default because David Axmark, one of the co-founders of MYSQL, is Swedish. No, really.)
We can fix this on the database connection level by setting names:
(clsql:execute-command "SET NAMES 'utf8mb4'")
And now when we query charsets again they're utf8!
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "utf8mb4_general_ci")
;; ("CHARACTER_SET_CONNECTION" "utf8mb4")
;; ("CHARACTER_SET_RESULTS" "utf8mb4")
;; ("CHARACTER_SET_CLIENT" "utf8mb4"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
However, this has to be set per-database connection. Really, we want to be using utf8 by default, because it's fucking 2020 and utf8 has been the standard character encoding for over a decade.
You can find you mariadb config files with this shell command:
$ mysql --help --verbose | grep -A 1 "Default options"
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
... then add add these values under their respective headings in one of those files and hey presto, you're charsetting like it's 2008!
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
collation-server = utf8mb4_unicode_ci
init-connect= 'SET NAMES utf8mb4'
character-set-server = utf8mb4
Now the charset for our initial connection should be set to utf8, and we're free to hack away~ "#.chars();
for ch in input
{
if !small.contains_key(&ch) {
small.insert(ch, 0);
} else {
*small.get_mut(&ch).unwrap() += 1;
}
if !hash.contains_key(&ch) {
hash.insert(ch, 0);
} else {
*hash.get_mut(&ch).unwrap() += 1;
}
}
let mut op1: Vec<_> = small.into_iter().collect();
let mut op2: Vec<_> = hash.into_iter().collect();
op1.sort();
op2.sort();
assert_eq!(&op1[..], &op2[..]);
}
#[cfg(nightly)]
#[bench]
fn char_smallmap(b: &mut Bencher)
{
let mut map =Map::new();
let nm: Vec<char> = r#"
E : boku (at) plum (dot) moe (PGP Key)
bantflags
words
stream"#.chars().collect();
let mut i=0;
b.iter(|| {
black_box(map.insert(nm[i], 100usize));
if i == nm.len()-1 {
i =0
} else {
i+=1;
}
})
}
#[cfg(nightly)]
#[bench]
fn u8_smallmap(b: &mut Bencher)
{
let mut map =Map::new();
let mut i=0u8;
b.iter(|| {
black_box(map.insert(i, 100usize));
if i == 255 {
i=0
} else {
i+=1;
}
})
}
use std::collections::HashMap;
#[cfg(nightly)]
#[bench]
fn char_map(b: &mut Bencher)
{
let mut map =HashMap::new();
let nm: Vec<char> = r#"
E : boku (at) plum (dot) moe (PGP Key)
bantflags
words
stream"#.chars().collect();
let mut i=0;
b.iter(|| {
black_box(map.insert(nm[i], 100usize));
if i == nm.len()-1 {
i =0
} else {
i+=1;
}
})
}
#[cfg(nightly)]
#[bench]
fn u8_map(b: &mut Bencher)
{
let mut map =HashMap::new();
let mut i=0u8;
b.iter(|| {
black_box(map.insert(i, 100usize));
if i == 255 {
i=0
} else {
i+=1;
}
})
}
#[cfg(nightly)]
#[bench]
fn smap_bench(b: &mut Bencher)
{
let mut small = Map::new();
let input = r#"Bludgeoning clsql and mariadb until they kind of work
There are no good mysql connectors for Common Lisp, whatever you decide to use, you're not going to have a good time. clsql is pretty much the best we've got, and since I had such a delightful experience configuring it myself, I hope to smooth the process of getting this shitware up and running with my own little blogpost here.
connecting to a database
Connecting to mysql with clsql looks something like this
(clsql:connect
'("host" "database" "username" "password" &optional "port") ;; connection-spec
:database-type :mysql) ;; Defaults to clsql:*default-database-type*
clsql builds ffi libraries the first time you try to connect to a database, and you can get some pretty arcane error messages if you don't have the right shared libraries installed already. These are (hopefully) packaged by your distro already, in Gentoo you want
$ sudo emerge --ask dev-db/mariadb-connector-c
in openSUSE it's
$ zypper in mariadb-connector-odbc
if you're on Debian or Ubuntu then God save your soul, etc.
Despite mariadb being a drop-in replacement for mysql, the clsql developers have made little to no effort to actually support it, to the point where mariadb's versioning system isn't even accounted for when loading the libraries, causing clsql to just error out for absolutely no reason. (actually, this shitware doesn't even 'support' modern versions on mysql because the version numbers have increased above what they check for, but they load fine as well)
We can modify the version checking script, to fix this, which you can find here: ~/quicklisp/dists/quicklisp/software/clsql-*-git/db-mysql/mysql-client-info.lisp (If your quicklisp is installed in its default directory you can paste this straight into a terminal or VIM or some such and it'll expand itself, which is kinda neat.)
If we add the top three lines just before the bottom two it'll stopping giving you stupid error messages when you try and connect.
((and (eql (schar *mysql-client-info* 0) #\1)
(eql (schar *mysql-client-info* 0) #\0))
(pushnew :mysql-client-v6 cl:*features*))
(t
(error "Unknown mysql client version '~A'." *mysql-client-info*)))))
charset issues
After (finally) connecting, when we first query the database we might get an error like this:
debugger invoked on a BABEL-ENCODINGS:INVALID-UTF8-STARTER-BYTE in thread
#<THREAD "main thread" RUNNING {B3E2151}>: Illegal :UTF-8 character starting at position 18.
We can identify the issue like so:
(defvar charsets
"SELECT VARIABLE_NAME, SESSION_VALUE \
FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES \
WHERE VARIABLE_NAME LIKE 'character_set_c%' \
OR VARIABLE_NAME LIKE 'character_set_re%' \
OR VARIABLE_NAME LIKE 'collation_c%';")
;; CHARSETS
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "latin1_swedish_ci")
;; ("CHARACTER_SET_CONNECTION" "latin1")
;; ("CHARACTER_SET_RESULTS" "latin1")
;; ("CHARACTER_SET_CLIENT" "latin1"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
These are actually the default collation and character sets in MYSQL; Smarter mysql drivers would set their charset and collation to utf8 by themseleves, but not clsql! (the collation is set to latin1_swedish_ci by default because David Axmark, one of the co-founders of MYSQL, is Swedish. No, really.)
We can fix this on the database connection level by setting names:
(clsql:execute-command "SET NAMES 'utf8mb4'")
And now when we query charsets again they're utf8!
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "utf8mb4_general_ci")
;; ("CHARACTER_SET_CONNECTION" "utf8mb4")
;; ("CHARACTER_SET_RESULTS" "utf8mb4")
;; ("CHARACTER_SET_CLIENT" "utf8mb4"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
However, this has to be set per-database connection. Really, we want to be using utf8 by default, because it's fucking 2020 and utf8 has been the standard character encoding for over a decade.
You can find you mariadb config files with this shell command:
$ mysql --help --verbose | grep -A 1 "Default options"
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
... then add add these values under their respective headings in one of those files and hey presto, you're charsetting like it's 2008!
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
collation-server = utf8mb4_unicode_ci
init-connect= 'SET NAMES utf8mb4'
character-set-server = utf8mb4
Now the charset for our initial connection should be set to utf8, and we're free to hack away~ "#;
b.iter(|| {
for ch in input.chars()
{
if !small.contains_key(&ch) {
small.insert(ch, 0);
} else {
*small.get_mut(&ch).unwrap() += 1;
}
}
})
}
#[cfg(nightly)]
#[bench]
fn hmap_bench(b: &mut Bencher)
{
let mut small = HashMap::new();
let input = r#"Bludgeoning clsql and mariadb until they kind of work
There are no good mysql connectors for Common Lisp, whatever you decide to use, you're not going to have a good time. clsql is pretty much the best we've got, and since I had such a delightful experience configuring it myself, I hope to smooth the process of getting this shitware up and running with my own little blogpost here.
connecting to a database
Connecting to mysql with clsql looks something like this
(clsql:connect
'("host" "database" "username" "password" &optional "port") ;; connection-spec
:database-type :mysql) ;; Defaults to clsql:*default-database-type*
clsql builds ffi libraries the first time you try to connect to a database, and you can get some pretty arcane error messages if you don't have the right shared libraries installed already. These are (hopefully) packaged by your distro already, in Gentoo you want
$ sudo emerge --ask dev-db/mariadb-connector-c
in openSUSE it's
$ zypper in mariadb-connector-odbc
if you're on Debian or Ubuntu then God save your soul, etc.
Despite mariadb being a drop-in replacement for mysql, the clsql developers have made little to no effort to actually support it, to the point where mariadb's versioning system isn't even accounted for when loading the libraries, causing clsql to just error out for absolutely no reason. (actually, this shitware doesn't even 'support' modern versions on mysql because the version numbers have increased above what they check for, but they load fine as well)
We can modify the version checking script, to fix this, which you can find here: ~/quicklisp/dists/quicklisp/software/clsql-*-git/db-mysql/mysql-client-info.lisp (If your quicklisp is installed in its default directory you can paste this straight into a terminal or VIM or some such and it'll expand itself, which is kinda neat.)
If we add the top three lines just before the bottom two it'll stopping giving you stupid error messages when you try and connect.
((and (eql (schar *mysql-client-info* 0) #\1)
(eql (schar *mysql-client-info* 0) #\0))
(pushnew :mysql-client-v6 cl:*features*))
(t
(error "Unknown mysql client version '~A'." *mysql-client-info*)))))
charset issues
After (finally) connecting, when we first query the database we might get an error like this:
debugger invoked on a BABEL-ENCODINGS:INVALID-UTF8-STARTER-BYTE in thread
#<THREAD "main thread" RUNNING {B3E2151}>: Illegal :UTF-8 character starting at position 18.
We can identify the issue like so:
(defvar charsets
"SELECT VARIABLE_NAME, SESSION_VALUE \
FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES \
WHERE VARIABLE_NAME LIKE 'character_set_c%' \
OR VARIABLE_NAME LIKE 'character_set_re%' \
OR VARIABLE_NAME LIKE 'collation_c%';")
;; CHARSETS
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "latin1_swedish_ci")
;; ("CHARACTER_SET_CONNECTION" "latin1")
;; ("CHARACTER_SET_RESULTS" "latin1")
;; ("CHARACTER_SET_CLIENT" "latin1"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
These are actually the default collation and character sets in MYSQL; Smarter mysql drivers would set their charset and collation to utf8 by themseleves, but not clsql! (the collation is set to latin1_swedish_ci by default because David Axmark, one of the co-founders of MYSQL, is Swedish. No, really.)
We can fix this on the database connection level by setting names:
(clsql:execute-command "SET NAMES 'utf8mb4'")
And now when we query charsets again they're utf8!
(clsql:query charsets)
;; (("COLLATION_CONNECTION" "utf8mb4_general_ci")
;; ("CHARACTER_SET_CONNECTION" "utf8mb4")
;; ("CHARACTER_SET_RESULTS" "utf8mb4")
;; ("CHARACTER_SET_CLIENT" "utf8mb4"))
;; ("VARIABLE_NAME" "SESSION_VALUE")
However, this has to be set per-database connection. Really, we want to be using utf8 by default, because it's fucking 2020 and utf8 has been the standard character encoding for over a decade.
You can find you mariadb config files with this shell command:
$ mysql --help --verbose | grep -A 1 "Default options"
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
... then add add these values under their respective headings in one of those files and hey presto, you're charsetting like it's 2008!
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
collation-server = utf8mb4_unicode_ci
init-connect= 'SET NAMES utf8mb4'
character-set-server = utf8mb4
Now the charset for our initial connection should be set to utf8, and we're free to hack away~ "#;
b.iter(|| {
for ch in input.chars()
{
let counter = small.entry(ch).or_insert(0);
*counter +=1;
}
})
}
}
mod tests;

@ -2,20 +2,8 @@
use super::*;
macro_rules! collapse {
(unsafe $type:ty) => {
impl Collapsible for $type
{
#[inline] fn collapse(&self) -> u8
{
collapse(unsafe {
std::slice::from_raw_parts(self as *const Self as *const u8, std::mem::size_of_val::<Self>(self))
})
}
}
};
(char) => {
impl Collapsible for char
impl Collapse for char
{
#[inline] fn collapse(&self) -> u8
{
@ -24,7 +12,7 @@ macro_rules! collapse {
}
};
($type:ty) => {
impl Collapsible for $type
impl Collapse for $type
{
#[inline] fn collapse(&self) -> u8
{
@ -35,14 +23,14 @@ macro_rules! collapse {
};
}
impl Collapsible for bool
impl Collapse for bool
{
#[inline] fn collapse(&self) -> u8
{
*self as u8
}
}
impl Collapsible for u8
impl Collapse for u8
{
#[inline] fn collapse(&self) -> u8
{
@ -50,14 +38,13 @@ impl Collapsible for u8
}
}
impl Collapsible for i8
impl Collapse for i8
{
#[inline] fn collapse(&self) -> u8
{
*self as u8
}
}
collapse!(char);
collapse!(u16);
collapse!(i16);

@ -0,0 +1,192 @@
//! Tests
use super::*;
use std::collections::{
HashMap,
BTreeMap,
};
#[test]
fn it_works()
{
let mut map = Map::new();
map.insert('>', false);
map.insert('<', true);
map.insert('ł', true);
let clone = map.clone();
for (k, v) in clone.into_iter()
{
assert!(map.get(&k) == Some(&v))
}
}
macro_rules! test_insert_type {
($ty:ty, $data:expr) => {
{
print!("Testing {}... ", std::any::type_name::<$ty>());
let mut small = Map::new();
let mut hash = HashMap::new();
for (i,x) in (0..).zip($data)
{
small.insert(x, i);
hash.insert(x, i);
}
assert_eq!(small.len(), hash.len());
for (k,v) in hash.iter()
{
assert_eq!(v, small.get(k).unwrap());
}
println!("OK");
}
};
($ty:tt) => {
test_insert_type!($ty, $ty::MIN..$ty::MAX);
}
}
#[test]
fn type_char()
{
test_insert_type!(char, TEST_STRING.chars());
}
#[test]
fn type_primitive()
{
test_insert_type!(i8, TEST_STRING.chars());
test_insert_type!(u8);
test_insert_type!(i16);
test_insert_type!(u16);
test_insert_type!(i32, -100..1000);
test_insert_type!(u32, 0..10000);
test_insert_type!(u64, -100..1000);
test_insert_type!(i64, 0..10000);
test_insert_type!(u128, -100..1000);
test_insert_type!(i128, 0..10000);
}
#[cfg(nightly)]
mod benchmarks
{
use super::*;
use test::{Bencher, black_box};
macro_rules! map_bench {
($b:expr, $map:ident) => {
let mut map = $map::new();
$b.iter(|| {
for chr in TEST_STRING.chars()
{
if let Some(ent) = map.get_mut(&chr) {
*ent += 1;
} else {
black_box(map.insert(chr, 0));
}
}
})
};
}
macro_rules! ent_bench {
($b:expr, $map:ident) => {
let mut map = $map::new();
$b.iter(|| {
for chr in TEST_STRING.chars()
{
black_box(*map.entry(chr).or_insert(0usize) += 1);
}
})
};
}
#[bench]
fn es_char(b: &mut Bencher)
{
ent_bench!(b, Map);
}
#[bench]
fn eh_char(b: &mut Bencher)
{
ent_bench!(b, HashMap);
}
#[bench]
fn eb_char(b: &mut Bencher)
{
ent_bench!(b, BTreeMap);
}
#[bench]
fn s_char(b: &mut Bencher)
{
map_bench!(b, Map);
}
#[bench]
fn h_char(b: &mut Bencher)
{
map_bench!(b, HashMap);
}
#[bench]
fn b_char(b: &mut Bencher)
{
map_bench!(b, BTreeMap);
}
}
const TEST_STRING: &str = r#"
"#;
Loading…
Cancel
Save