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;