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
// (c) 2018 Tuan Tran. Licensed under MIT License. /*! **NOTICE: THE LIBRARY IS STILL EXPERIMENTAL AND VERY BUGGY. API IS INCOMPLETE AND SUBJECTED TO CHANGE. PLEASE PROCEED WITH CAUTION** This crate is an implementation of ObjectSpace - a natural progression on the idea of [TupleSpace](https://en.wikipedia.org/wiki/Tuple_space) proposed by Gelernter in 1985. ObjectSpace is a data structure with the capability of holding any structure (e.g: a string, an int, and a complex struct could all lives under one ObjectSpace). It also allows retrieving a struct based on the value of a field. This crate also provides a fully thread-safe implementation of ObjectSpace, which allows simple concurrent and distributed programming. # API An ObjectSpace could perform the following tasks: - `write` an object to the space. E.g: `space.write(test_struct)` - `try_read` (non-blocking), `read` (blocking), and `read_all` structs of a type. E.g: `space.try_read::<TestStruct>()` - `try_take`, `take`, and `take_all` to remove and returns struct of a type. E.g: `space.try_take::<TestStruct>()` Notice that an ObjectSpace could hold data from any types, which means that an i64, a String, and a complex struct could all live under one space (which leads to the somewhat wordy API for retrieving items). Additionally, by implementing `ValueLookupObjectSpace` and `RangeLookupObjectSpace`, an ObjectSpace could retrieve item based on the value of a field. Notice that the field must be a "basic" field: the type of the field must be either an int, a string, or a bool. E.g: Given a TestStruct: ```rust struct Prop { touched: bool } struct TestStruct { index: i32, property: Prop, } ``` `space.try_take_by_value::<TestStruct>("property.touched", true)` will return a `TestStruct` with the value `true` for `property.touched`. `space.try_take_by_range::<TestStruct>("index", 2..10)` will return a `TestStruct` with the value of `index` between in the range `2..10` For further information, please read the documentation of `ObjectSpace`, `RangeLookupObjectSpace`, and `ValueLookupObjectSpace` # TreeObjectSpace `TreeSpaceObject` is a referenced implementation of `ObjectSpace` trait. It is, in essence, a concurrent HashMap of `TypeId` and corresponding `Entry` for each type. Each `Entry` stores objects by serializing & flattening their structure, then put the values of basic fields in a `BTreeMap` for efficient lookup. `TreeSpaceObject` is thread-safe, which allows it to be used in concurrent and distributed settings. # Example Here is a program to calculate all primes up to a limit using ObjectSpace ```rust extern crate object_space; extern crate serde; #[macro_use] extern crate serde_derive; use std::thread; use std::env; use std::sync::Arc; use object_space::{ObjectSpace, ValueLookupObjectSpace, RangeLookupObjectSpace, TreeObjectSpace}; fn main() { let mut args = env::args(); let upper_lim = 1000000; let thread_count = 4; // setup. add 2 & 3 just because we can let mut n = 4; let space = Arc::new(TreeObjectSpace::new()); space.write::<i64>(2); space.write::<i64>(3); // create 4 worker threads for _ in 0..thread_count { let space_clone = space.clone(); thread::spawn(move || { check_numbers(space_clone); }); } // continue until we hit limit while n < upper_lim { let max = if n * n < upper_lim { n * n } else { upper_lim }; for i in 0..thread_count { // divide work evenly between threads let start = n + (((max - n) as f64) / (thread_count as f64) * (i as f64)).round() as i64; let end = n + (((max - n) as f64) / (thread_count as f64) * ((i + 1) as f64)).round() as i64; let clone = space.clone(); clone.write(Task { finished: false, start: start, end: end, }); } // "joining" threads for _ in 0..thread_count { let clone = space.clone(); clone.take_by_value::<Task>("finished", &true); } n = max; } } fn check_numbers(space: Arc<TreeObjectSpace>) { loop { let task = space.take_by_value::<Task>("finished", &false); let max = task.end; let min = task.start; let primes: Vec<i64> = space.read_all::<i64>().filter(|i| i * i < max).collect(); for i in min..max { if primes.iter().all(|prime| i % prime != 0) { space.write(i); } } space.write(Task { finished: true, start: min, end: max, }); } } #[derive(Serialize, Deserialize)] struct Task { finished: bool, start: i64, end: i64, } ``` */ #![feature(collections_range)] extern crate chashmap; extern crate indexmap; extern crate ordered_float; extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_json; pub use self::object_space::*; mod entry; mod helpers; mod object_space;