tag_vec/
query.rs

1use crate::TagVec;
2use crate::BitField;
3use std::hash::Hash;
4
5/// Conveniences for creating expressions easily.
6/// The idea is to wildcard import this module in
7/// a confined scope, such that you get access to all the goodies
8/// within, but only for a short while, so that it
9/// doesn't reck the rest of your codebase.
10pub mod expressions {
11	use crate::query::Expression;
12	use std::hash::Hash;
13
14	/// Creates a tag expression
15	pub fn tag<'a, Q>(tag: &'a Q) -> Expression<'a, Q>
16			where Q: ?Sized + Eq + Hash {
17		Expression::Tag(tag)
18	}
19
20	/// Creates an and expression
21	pub fn and<'a, Q>(a: Expression<'a, Q>, b: Expression<'a, Q>) -> Expression<'a, Q>
22			where Q: ?Sized + Eq + Hash {
23		Expression::And(Box::new(a), Box::new(b))
24	}
25
26	/// Creates an or expression
27	pub fn or<'a, Q>(a: Expression<'a, Q>, b: Expression<'a, Q>) -> Expression<'a, Q>
28			where Q: ?Sized + Eq + Hash {
29		Expression::Or(Box::new(a), Box::new(b))
30	}
31
32	/// Create a not expression
33	pub fn not<'a, Q>(a: Expression<'a, Q>) -> Expression<'a, Q>
34			where Q: ?Sized + Eq + Hash {
35		Expression::Not(Box::new(a))
36	}
37}
38
39/// A definition of a query
40pub enum Expression<'a, Q> where Q: ?Sized + Hash + Eq + 'a {
41	And(Box<Expression<'a, Q>>, Box<Expression<'a, Q>>),
42	Or(Box<Expression<'a, Q>>, Box<Expression<'a, Q>>),
43	Not(Box<Expression<'a, Q>>),
44	Tag(&'a Q),
45}
46
47impl<'a, Q: ?Sized + Hash + Eq + 'a> Expression<'a, Q> {
48	/// Returns the number of commands it takes
49	/// to represent the command
50	fn command_size(&self) -> usize {
51		use Expression::*;
52
53		match self {
54			And(a, b) => 1 + a.command_size() + b.command_size(),
55			Or(a, b) => 1 + a.command_size() + b.command_size(),
56			Not(a) => 1 + a.command_size(),
57			Tag(_) => 1,
58		}
59	}
60
61	/// Converts this expression to a series of commands
62	fn to_commands(self, tag_requests: &mut Vec<&'a Q>, commands: &mut Vec<u16>) {
63		use Expression::*;
64
65		match self {
66			And(a, b) => {
67				a.to_commands(tag_requests, commands);
68				b.to_commands(tag_requests, commands);
69				commands.push(QUERY_CMD_AND);
70			},
71			Or(a, b) => {
72				a.to_commands(tag_requests, commands);
73				b.to_commands(tag_requests, commands);
74				commands.push(QUERY_CMD_OR);
75			},
76			Not(a) => {
77				a.to_commands(tag_requests, commands);
78				commands.push(QUERY_CMD_NOT);
79			},
80			Tag(tag) => {
81				let id = tag_requests.len() as u16;
82				assert!(id < QUERY_CMD_TAG);
83				tag_requests.push(tag);
84				// Any command with an id lower than QUERY_CMD_TAG is
85				// a get tag command.
86				commands.push(id);
87			},
88		}
89	}
90}
91
92// Define command constants. This cannot be represented as an
93// enum because the last property can be any value lower than QUERY_CMD_TAG
94const QUERY_CMD_AND: u16 = 0xFFFF;
95const QUERY_CMD_OR: u16 = 0xFFFD;
96const QUERY_CMD_NOT: u16 = 0xFFFC;
97const QUERY_CMD_TAG: u16 = 0xFFFC; // Less than, not equals
98
99/// A Query iterator. Will iterate over the elements of a TagVec
100/// that fulfill a requirement, defined by the "Expression" enum.
101pub struct Query<'a, F> 
102		where 
103				F: BitField {
104	tag_data: Vec<Option<&'a [F]>>,
105	commands: Vec<u16>,
106	bit_ctr: usize,
107	total_bits: usize,
108	data: F,
109	stack: Vec<F>,
110}
111
112impl<'a, F> Query<'a, F> 
113		where 
114				F: BitField
115{
116	/// Creates a Query from an expression. This makes the representation of
117	/// the query significantly more efficient(should be better at cache locality)
118	/// The expressions are converted into "commands", and commands work like this:
119	/// We have a list of commands. Each command is run once for every BitField in
120	/// the BitField slices. These commands pop the data they need of the stack,
121	/// and push the result. 
122	/// The last command will give exactly one result left
123	/// on the stack, and that result is the BitField for the next couple of
124	/// elements that fulfill the requirement.
125	pub(crate) fn create_from<T, Q>(vec: &'a TagVec<T, F>, expr: Expression<'a, Q>) 
126			-> Query<'a, F> 
127				where T: Eq + Hash + Clone + std::borrow::Borrow<Q>, 
128						Q: ?Sized + Eq + Hash + 'a {
129		let mut tag_requests = Vec::new();
130		let mut commands = Vec::with_capacity(expr.command_size());
131
132		expr.to_commands(&mut tag_requests, &mut commands);
133
134		// Get references to the data storing the things
135		// the commands want
136		let tag_data: Vec<_> = tag_requests.into_iter()
137				.map(|request| vec.tag_fields.get(request).map(|v| v.data())).collect();
138
139		Query {
140			tag_data,
141			commands,
142			bit_ctr: 0,
143			total_bits: vec.len(),
144			data: F::empty(),
145			stack: Vec::new(),
146		}
147	}
148
149	/// Assumes that there is another element.
150	/// Also, only returns a bool, wether or not
151	/// the condition was fulfilled the next iteration
152	fn sloppy_next(&mut self) -> bool {
153		let local_index = self.bit_ctr % F::n_bits();
154
155		if local_index == 0 {
156			// We are on a new bit! Evaluate the local BitField first
157			let data_index = self.bit_ctr / F::n_bits();
158
159			// We assume that the stack is always sufficiently populated, because the "to_commands"
160			// function shouldn't generate commands that break this
161			self.stack.clear();
162			let stack = &mut self.stack;
163			let commands = &self.commands;
164			let tag_data = &self.tag_data;
165			for cmd in commands.iter() {
166				match *cmd {
167					QUERY_CMD_AND => {
168						let a = stack.pop().unwrap();
169						let b = stack.pop().unwrap();
170						stack.push(a & b);
171					},
172					QUERY_CMD_OR => {
173						let a = stack.pop().unwrap();
174						let b = stack.pop().unwrap();
175						stack.push(a | b);
176					},
177					QUERY_CMD_NOT => {
178						let a = stack.pop().unwrap();
179						stack.push(!a);
180					},
181					tag => {
182						// It's definitely a tag
183						stack.push(tag_data[tag as usize].map_or(F::empty(), |v| v[data_index]));
184					},
185				}
186			}
187
188			self.data = stack[0];
189		}
190
191		self.bit_ctr += 1;
192		self.data.get_bit(local_index)
193	}
194}
195
196impl<'a, F> Iterator for Query<'a, F> where F: BitField {
197	type Item = usize;
198
199	fn next(&mut self) -> Option<usize> {
200		while self.bit_ctr < self.total_bits {
201			if self.sloppy_next() {
202				// The bit_ctr has been increased in sloppy_next, so 
203				// we have to return the previous one
204				return Some(self.bit_ctr - 1);
205			}
206		}
207
208		None
209	}
210}