object_space/
lib.rs

1// (c) 2018 Tuan Tran. Licensed under MIT License.
2
3/*!
4**NOTICE: THE LIBRARY IS STILL EXPERIMENTAL AND VERY BUGGY. API IS INCOMPLETE AND SUBJECTED TO CHANGE. PLEASE PROCEED WITH CAUTION**
5
6This 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.
7
8This crate also provides a fully thread-safe implementation of ObjectSpace, which allows simple concurrent and distributed programming.
9
10# API
11
12An ObjectSpace could perform the following tasks:
13
14- `write` an object to the space. E.g: `space.write(test_struct)`
15- `try_read` (non-blocking), `read` (blocking), and `read_all` structs of a type. E.g: `space.try_read::<TestStruct>()`
16- `try_take`, `take`, and `take_all` to remove and returns struct of a type. E.g: `space.try_take::<TestStruct>()`
17
18Notice 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).
19
20Additionally, 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.
21
22E.g: Given a TestStruct:
23
24```rust
25struct Prop {
26        touched: bool
27}
28
29struct TestStruct {
30    index: i32,
31    property: Prop,
32}
33```
34
35`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`
36
37For further information, please read the documentation of `ObjectSpace`, `RangeLookupObjectSpace`, and `ValueLookupObjectSpace`
38
39# TreeObjectSpace
40
41`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.
42
43# Example
44
45Here is a program to calculate all primes up to a limit using ObjectSpace
46 
47```rust
48extern crate object_space;
49extern crate serde;
50#[macro_use]
51extern crate serde_derive;
52
53use std::thread;
54use std::env;
55use std::sync::Arc;
56
57use object_space::{ObjectSpace, ValueLookupObjectSpace, RangeLookupObjectSpace, TreeObjectSpace};
58
59fn main() {
60    let mut args = env::args();
61    let upper_lim = 1000000;
62    let thread_count = 4;
63
64    // setup. add 2 & 3 just because we can
65    let mut n = 4;
66    let space = Arc::new(TreeObjectSpace::new());
67    space.write::<i64>(2);
68    space.write::<i64>(3);
69
70    // create 4 worker threads
71    for _ in 0..thread_count {
72        let space_clone = space.clone();
73        thread::spawn(move || {
74            check_numbers(space_clone);
75        });
76    }
77
78    // continue until we hit limit
79    while n < upper_lim {
80        let max = if n * n < upper_lim { n * n } else { upper_lim };
81
82        for i in 0..thread_count {
83            // divide work evenly between threads
84            let start =
85                n + (((max - n) as f64) / (thread_count as f64) * (i as f64)).round() as i64;
86            let end =
87                n + (((max - n) as f64) / (thread_count as f64) * ((i + 1) as f64)).round() as i64;
88
89            let clone = space.clone();
90            clone.write(Task {
91                finished: false,
92                start: start,
93                end: end,
94            });
95        }
96
97        // "joining" threads
98        for _ in 0..thread_count {
99            let clone = space.clone();
100            clone.take_by_value::<Task>("finished", &true);
101        }
102        n = max;
103    }
104}
105
106fn check_numbers(space: Arc<TreeObjectSpace>) {
107    loop {
108        let task = space.take_by_value::<Task>("finished", &false);
109        let max = task.end;
110        let min = task.start;
111        let primes: Vec<i64> = space.read_all::<i64>().filter(|i| i * i < max).collect();
112        for i in min..max {
113            if primes.iter().all(|prime| i % prime != 0) {
114                space.write(i);
115            }
116        }
117        space.write(Task {
118            finished: true,
119            start: min,
120            end: max,
121        });
122    }
123}
124
125#[derive(Serialize, Deserialize)]
126struct Task {
127    finished: bool,
128    start: i64,
129    end: i64,
130}
131```
132*/
133
134#![feature(collections_range)]
135extern crate chashmap;
136extern crate indexmap;
137extern crate ordered_float;
138extern crate serde;
139#[macro_use]
140extern crate serde_derive;
141extern crate serde_json;
142
143pub use self::object_space::*;
144mod entry;
145mod helpers;
146mod object_space;