1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/// Libre Geolocation - server datastore
// Copyright 2024 Federico Ceratto <federico.ceratto@posteo.com>
// Released under AGPL
//
use anyhow::Result;
use bincode::{deserialize, serialize};
use log::info;
use serde::{Deserialize, Serialize};
// use sled::Db;
use std::fs;
use std::path::Path;

use libreloc_shared::*;

// pub struct SledDatastore {
//     incoming: Db,
//     wifi_bt: Db,
//     wifi_bt_maps: Db,
//     cells: Db,
// }

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct WiFiNode {
    pub location: String,
    pub fullhash: String, // secret
}

impl WiFiNode {
    fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
        serialize(&self)
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
        deserialize(bytes)
    }
}

// impl SledDatastore {
//     pub fn delete_all_data(&self) {
//         self.wifi_bt.clear();
//         self.cells.clear();
//         self.incoming.clear();
//     }

//     // pub fn get_incoming(&self, key: String) -> Option<WiFiNode> {
//     //     if let Ok(Some(data)) = self.incoming.get(key) {
//     //         if let Ok(my_data) = deserialize::<WiFiNode>(&data) {
//     //             return Some(my_data);
//     //         }
//     //     }
//     //     None
//     // }

//     // pub fn set_incoming(&self, key: &[u8], my_data: &WiFiNode) {
//     //     let bytes = serialize(my_data).unwrap();
//     //     self.incoming.insert(key, bytes).unwrap();
//     // }

//     // WiFi: get
//     pub fn get_wifi(&self, key: &str) -> Option<WiFiNode> {
//         if let Ok(Some(data)) = self.wifi_bt.get(key) {
//             if let Ok(my_data) = deserialize::<WiFiNode>(&data) {
//                 return Some(my_data);
//             }
//         }
//         None
//     }

//     // WiFi: set
//     pub fn set_wifi(&self, key: &str, my_data: &WiFiNode) {
//         let bytes = serialize(my_data).unwrap();
//         self.wifi_bt.insert(key, bytes).unwrap();
//     }
// }

// pub fn open_sled(datadir: &str) -> Result<SledDatastore> {
//     let path = Path::new(datadir); //.join("sled");
//     fs::create_dir_all(path)?;

//     info!("Opening {:?}", path.join("s1"));

//     let incoming = sled::open(path.join("incoming"))?;
//     let wifi_bt = sled::open(path.join("wifi_bt"))?;
//     let wifi_bt_maps = sled::open(path.join("wifi_bt_maps"))?;
//     let cells = sled::open(path.join("cells"))?;
//     Ok(SledDatastore {
//         incoming,
//         wifi_bt,
//         wifi_bt_maps,
//         cells,
//     })
// }

// In-memory

use std::collections::BTreeMap;
use std::collections::{HashMap, HashSet};

// pub type FP0 = [u8; 2];
// pub type Lok0 = [u8; 2];
// pub type MiniMap0 = HashMap<Lok0, u16>;

// pub type FP1 = [u8; 2]; // len of Lok0 + FP0
// pub type Lok1 = [u8; 2];
// pub type MiniMap1 = HashMap<Lok1, u16>;

pub struct MemDatastore {
    // pub wifi_bt: BTreeMap<String, WiFiNode>,
    wifi_bt_map: BTreeMap<String, u8>,
    pub wifi_bt_bh: BloomHashStore,
    // pub wifi_bt_map0: HashMap<FP0, HashSet<String>>,
}

// Type inference lets us omit an explicit type signature (which
// would be `BTreeMap<String, String>` in this example).

pub fn open_mem_datastore() -> MemDatastore {
    MemDatastore {
        // wifi_bt: BTreeMap::new(),
        wifi_bt_map: BTreeMap::new(),
        wifi_bt_bh: BloomHashStore::new(),
        // wifi_bt_map0: HashMap::new(),
    }
}

impl MemDatastore {
    pub fn insert_blip(&mut self, blip: &Blip) {
        // TODO
    }

    /// Insert "raw" datapoint (lat, lon, macaddr...)
    pub fn insert_datapoint(&mut self, lat: f64, lon: f64, mac: &Mac, ssid: &str) {
        //
        let c = geohash::Coord { x: lon, y: lat };
        let geoh = coord_to_geohash(c);
        for s in GEOHASH_DEPTHS {
            // The same blip generates multiple fingerprints that as needed by clients
            // doing location at different geohash-depths
            let geopath = &geoh[..s];
            let fp = generate_fingerprint_wifi(&geopath, mac, ssid);
            let b = geoh[s..s + 2].as_bytes();
            let geosegment: GeoSegment = [b[0], b[1]];
            self.wifi_bt_bh.insert(&fp, &geosegment)
        }
    }

    pub fn increment_wifi_map(&mut self, k: &str) {
        // *self.wifi_bt_map.entry(k.to_string()).or_insert(0) += 1;
        self.wifi_bt_map.insert(k.to_owned(), 1);
    }

    /// Extract a minimap representing a geographical map of all devices
    /// matching a fingerprint
    pub fn scan_minimap(&self, prefix: &str, skip: usize) -> MiniMap {
        let start = prefix.to_string();
        let mut end = prefix.to_string();
        end.push(char::MAX);
        assert!(
            matches!(prefix.len(), 9 | 6 | 3 | 7 | 4),
            "unexpected len {}",
            prefix
        );

        self.wifi_bt_map
            .range(start..end)
            .map(|(k, v)| (k.chars().skip(skip).collect(), *v))
            .collect()
    }
}