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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Documentation for rust_storage_interface_library
//!
//! Load i32
//! # Examples
//! ```
//! let my_previously_stored_i32_value: i32 = rust_storage_interface_library::load::loadi32(my_previously_saved_key: i64)
//! ```
//!
//! Load i64
//! # Examples
//! ```
//! let my_previously_stored_i64_value: i64 = rust_storage_interface_library::load::loadi64(my_previously_saved_key)
//! ```
//!
//! Store i32
//! Stores a single i32 value and returns a key which can be used to fetch the stored i32 value at a later date
//! # Examples
//! ```
//! my_i32_to_store: i32 = 88;
//! my_new_key: i64 = rust_storage_interface_library::store::storei32(my_i32_to_store)
//!
//! ```
//!
//! Store i64
//! Stores a single i64 value and returns a key which can be used to fetch the stored i64 value at a later date
//! # Examples
//! ```
//! my_i64_to_store: i64 = 88;
//! my_new_key: i64 = rust_storage_interface_library::store::storei64(my_i64_to_store)
//!
//! ```

mod load {
    // Documentation for load module
    /// Load i32
    /// # Examples
    /// ```
    /// let my_previously_stored_i32_value: i32 = rust_storage_interface_library::load::loadi32(my_previously_saved_key: i64)
    /// ```
    pub fn load_single_i32(_key: i64) -> i32 {
        // TODO - will require the syntax to interact with SecondState's other native library for SSVM which provides the database interaction
        // placeholder for now is 1
        1
    }

    /// Load i64
    /// # Examples
    /// ```
    /// let my_previously_stored_i64_value: i64 = rust_storage_interface_library::load::loadi64(my_previously_saved_key)
    /// ```
    pub fn load_single_i64(_key: i64) -> i64 {
        // TODO - will require the syntax to interact with SecondState's other native library for SSVM which provides the database interaction
        // placeholder for now is 1
        1
    }

    pub fn load_string(_key: i64) -> String {
        // The first thing we do is call SSVM database using the key and this will return (a list of i32s)
        // Just for now though, the test i32 is currently 7170388 which is equivalent to the String "Tim". Obviouisly longer strings will be stored in many i32s
        // For strings which are stored across many i32s we will be calling the database many times using the master key using its sequential suffix
        let _call_database_using_key: i32 = 7170388;
        // We need to convert each i32 to little endian bytes like this
        let value_as_le_bytes = _call_database_using_key.to_le_bytes();
        // TODO use the i32 that we fetched instead of this concrete value 7170388 above
        println!(
            "The i32 integer as little endian bytes: {:?}",
            value_as_le_bytes
        );
        // Output
        // The i32 integer as little endian bytes: [84, 105, 109, 0]

        // We now get the bytes into a vector and clean out the zeros that we may have used to fill the batch
        let mut vec_for_string = value_as_le_bytes.to_vec();
        while vec_for_string.get(vec_for_string.len() - 1) == Some(&0) {
            println!("Found a zero at position: {:?}", vec_for_string.len() - 1);
            vec_for_string.remove(vec_for_string.len() - 1);
        }
        // Output
        // Found a zero at position: 3

        // Lastly we pass the vec into the String::from_utf8
        let re_string = String::from_utf8(vec_for_string).unwrap();
        println!("String of bytes as string again: {:?}", re_string);
        // Output
        // String of bytes as string again: "Tim"
        String::from("Tim") // Placeholder
    }
}

mod store {
    
    use rand::Rng;
    extern crate rand;
    use std::time::SystemTime;
    use std::convert::TryInto;

    /// Get the system time in seconds (epoch) which produces a 10 digit number
    pub fn get_time() -> Result<u64, String> {
        match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
            Ok(n) => {println!("Generated 10 digit time epoch: {:?}", n.as_secs());
            Ok(n.as_secs())},
            Err(_) => Err("Unable to get system time".to_string()),
        }
    }

    // Creates an absolute (non negative) random number which is a fixed length. Specifically, one character less than the length of the upper bound
    pub fn create_fixed_length_random_number(_lower_bound: i64, _upper_bound: i64) -> Result<i64, rand::Error> {
        let length: u64 = (_upper_bound.to_string().len() - 1).try_into().unwrap();
        let mut rng = rand::thread_rng();
        let mut my_rand: i64 = rng.gen_range(_lower_bound, _upper_bound).abs();
        while my_rand.to_string().len() < length.try_into().unwrap() {
            my_rand = my_rand + rng.gen_range(_lower_bound, _upper_bound - my_rand);
        }
        println!("Generated {:?} digit random number between {:?} and {:?}: {:?}", (_upper_bound.to_string().len() - 1), _lower_bound, _upper_bound - 1 , my_rand);
        Ok(my_rand)
    }

    /// Join the time and random numbers from above to form a unique 19 digit key (to completely fill an i64 variable)
    /// Also make sure that the number is absolute (not negative) and does not exceed the max value of i64 (which is 9223372036854775807)
    pub fn create_unique_key() -> Result<i64, String> {
        let a = format!("{:?}{:?}", get_time().unwrap(), create_fixed_length_random_number(1, 1000000000).unwrap());
        let my_int: i64 = a.parse().unwrap();
        assert!(my_int.is_positive());
        assert!(my_int <= i64::max_value());
        Ok(my_int)
    }

    /// Store i32
    /// Stores a single i32 value and returns a key which can be used to fetch the stored i32 value at a later date
    /// # Examples
    /// ```
    /// my_i32_to_store: i32 = 88;
    /// my_new_key: i64 = rust_storage_interface_library::store::storei32(my_i32_to_store)
    ///
    /// ```
    pub fn store_single_i32(_value: i32) -> i64 {
        let new_key: i64 = create_unique_key().unwrap();
        // TODO - will require the syntax to interact with SecondState's other native library for SSVM which provides the database interaction
        // placeholder for now is just returning a key via create_random_i64
        new_key
    }

    /// Store i64
    /// Stores a single i64 value and returns a key which can be used to fetch the stored i64 value at a later date
    /// # Examples
    /// ```
    /// my_i64_to_store: i64 = 88;
    /// my_new_key: i64 = rust_storage_interface_library::store::storei64(my_i64_to_store)
    ///
    /// ```
    pub fn store_single_i64(_value: i64) -> i64 {
        let new_key: i64 = create_unique_key().unwrap();
        // TODO - will require the syntax to interact with SecondState's other native library for SSVM which provides the database interaction
        // placeholder for now is just returning a key via create_random_i64
        new_key
    }

    // Just FYI, this will store Strings which consist of many i32 integers, not just "Tim". The user will still only get a single key and use that (internally we will have sequential suffix abstraction)
    pub fn store_string(_value: &str) -> i64 {
        let new_key: i64 = create_unique_key().unwrap();
        // Take an example string
        let raw_string = String::from(_value);
        // Start the storage transaction
        println!("Starting the storage transaction");
        // TODO SSVM::beginStoreTx()

        // Find the length of the raw string in terms of unicode scalar values
        //let raw_string_length: i32 = raw_string.chars().count();
        // Ensure that the number that represents the length of the string is going to fit inside a i32 variable
        //assert!(raw_string_length <= i32::max_value());
        // Also make sure that the number that represents the length of the string has not overflowed i.e. is greater than 2147483647
        
        // Place the length of the raw string into the first slot of the sequence
        //println!("Adding the length ( {:?} ) of the raw string to the first slot.", raw_string_length);
        // TODO SSVM::storei32(char as i32);

        // Storing each of the unicode scalar values to slots
        for single_char in raw_string.chars() {
            println!("Adding {:?}", single_char);
            // TODO SSVM:: storei32(char as i32);
        }
        // Return the new unique key to the calling code
        new_key
    }
}

// Test
#[cfg(test)]
mod tests {
    use super::store;
    #[test]
    fn test_get_time_is_ten_digits() {
        let num1: u64 = store::get_time().unwrap();
        assert_eq!(10, num1.to_string().len());
    }
    #[test]
    fn test_create_fixed_length_random_number_is_nine_digits() {
        let num2: i64 = store::create_fixed_length_random_number(1, 1000000000).unwrap();
        assert_eq!(9, num2.to_string().len());
    }
    #[test]
    fn test_create_fixed_length_random_number_is_gt_0() {
        let num3: i64 = store::create_fixed_length_random_number(-100, 1).unwrap();
        assert!(num3 > 0);
    }
    #[test]
    fn test_create_fixed_length_random_number_is_lt_1000000000() {
        let num4: i64 = store::create_fixed_length_random_number(1, 1000000000).unwrap();
        assert!(num4 < 999999999);
    }
    #[test]
    fn test_create_unique_key_in_nineteen_digits() {
        let num5: i64 = store::create_unique_key().unwrap();
        assert_eq!(19, num5.to_string().len());
    }
}