1 // Copyright (C) 2005 Nathaniel Smith <njs@pobox.com>
2 // 2008 Stephen Leake <stephen_leake@stephe-leake.org>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10
11 #ifndef __ROSTER_HH__
12 #define __ROSTER_HH__
13
14 #include "hybrid_map.hh"
15 #include "paths.hh"
16 #include "rev_types.hh"
17 #include "cset.hh" // need full definition of editable_tree
18
19 #include <stack>
20
21 struct node_id_source
22 {
23 virtual node_id next() = 0;
~node_id_sourcenode_id_source24 virtual ~node_id_source() {}
25 };
26
27 ///////////////////////////////////////////////////////////////////
28
29 node_id const the_null_node = 0;
30
31 inline bool
null_node(node_id n)32 null_node(node_id n)
33 {
34 return n == the_null_node;
35 }
36
37 template <> void dump(attr_map_t const & val, std::string & out);
38
39 enum roster_node_type { node_type_none, node_type_file, node_type_dir };
40
41 struct dfs_iter
42 {
43 const_dir_t root;
44 std::string curr_path;
45 bool return_root;
46 bool track_path;
47 std::stack< std::pair<const_dir_t, dir_map::const_iterator> > stk;
48
49 dfs_iter(const_dir_t r, bool t);
50 bool finished() const;
51 std::string const & path() const;
52 const_node_t operator*() const;
53 void operator++();
54
55 private:
56 void advance_top();
57 };
58
59 struct node
60 {
61 node();
62 node(node_id i);
63 node_id self;
64 node_id parent; // the_null_node iff this is a root dir
65 path_component name; // the_null_component iff this is a root dir
66 attr_map_t attrs;
67 roster_node_type type;
68 u32 cow_version; // see roster_t::cow_version below
69
70 // need a virtual function to make dynamic_cast work
71 virtual node_t clone() = 0;
~nodenode72 virtual ~node() {}
73 };
74
75
76 struct dir_node
77 : public node
78 {
79 dir_node();
80 dir_node(node_id i);
81 dir_map children;
82 bool has_child(path_component const & pc) const;
83 node_t get_child(path_component const & pc) const;
84 void attach_child(path_component const & pc, node_t child);
85 node_t detach_child(path_component const & pc);
86
87 // need a virtual function to make dynamic_cast work
88 virtual node_t clone();
~dir_nodedir_node89 virtual ~dir_node() {}
90 };
91
92
93 struct file_node
94 : public node
95 {
96 file_node();
97 file_node(node_id i, file_id const & f);
98 file_id content;
99
100 // need a virtual function to make dynamic_cast work
101 virtual node_t clone();
~file_nodefile_node102 virtual ~file_node() {}
103 };
104
105 inline bool
is_dir_t(const_node_t n)106 is_dir_t(const_node_t n)
107 {
108 return n->type == node_type_dir;
109 }
110
111 inline bool
is_file_t(const_node_t n)112 is_file_t(const_node_t n)
113 {
114 return n->type == node_type_file;
115 }
116
117 inline bool
is_root_dir_t(node_t n)118 is_root_dir_t(node_t n)
119 {
120 if (is_dir_t(n) && n->name.empty())
121 {
122 I(null_node(n->parent));
123 return true;
124 }
125
126 return false;
127 }
128
129 inline dir_t
downcast_to_dir_t(node_t const & n)130 downcast_to_dir_t(node_t const & n)
131 {
132 dir_t d = boost::dynamic_pointer_cast<dir_node, node>(n);
133 I(static_cast<bool>(d));
134 return d;
135 }
136
137 inline file_t
downcast_to_file_t(node_t const & n)138 downcast_to_file_t(node_t const & n)
139 {
140 file_t f = boost::dynamic_pointer_cast<file_node, node>(n);
141 I(static_cast<bool>(f));
142 return f;
143 }
144
145 inline const_dir_t
downcast_to_dir_t(const_node_t const & n)146 downcast_to_dir_t(const_node_t const & n)
147 {
148 const_dir_t d = boost::dynamic_pointer_cast<dir_node const, node const>(n);
149 I(static_cast<bool>(d));
150 return d;
151 }
152
153 inline const_file_t
downcast_to_file_t(const_node_t const & n)154 downcast_to_file_t(const_node_t const & n)
155 {
156 const_file_t f = boost::dynamic_pointer_cast<file_node const, node const>(n);
157 I(static_cast<bool>(f));
158 return f;
159 }
160
161 bool
162 shallow_equal(const_node_t a, const_node_t b,
163 bool shallow_compare_dir_children,
164 bool compare_file_contents = true);
165
166 template <> void dump(node_t const & n, std::string & out);
167
168 struct marking
169 {
170 u32 cow_version;
171 revision_id birth_revision;
172 std::set<revision_id> parent_name;
173 std::set<revision_id> file_content;
174 std::map<attr_key, std::set<revision_id> > attrs;
175 marking();
176 marking(marking const & other);
177 marking const & operator=(marking const & other);
operator ==marking178 bool operator==(marking const & other) const
179 {
180 return birth_revision == other.birth_revision
181 && parent_name == other.parent_name
182 && file_content == other.file_content
183 && attrs == other.attrs;
184 }
185 };
186
187 class marking_map
188 {
189 mutable u32 cow_version;
190 typedef cow_trie<node_id, marking_t, 8> map_type;
191 map_type _store;
192 public:
193 typedef map_type::key_type key_type;
194 typedef map_type::value_type value_type;
195
196 marking_map();
197 marking_map(marking_map const & other);
198 marking_map const & operator=(marking_map const & other);
199 const_marking_t get_marking(node_id nid) const;
200 marking_t const & get_marking_for_update(node_id nid);
201 void put_marking(node_id nid, marking_t const & m);
202 void put_marking(node_id nid, const_marking_t const & m);
203
204 // for roster_delta
205 void put_or_replace_marking(node_id nid, const_marking_t const & m);
206
207 typedef map_type::const_iterator const_iterator;
208 const_iterator begin() const;
209 const_iterator end() const;
210 size_t size() const;
211 bool contains(node_id nid) const;
212 void clear();
213 void remove_marking(node_id nid);
214 };
215
216 template <> void dump(std::set<revision_id> const & revids, std::string & out);
217 template <> void dump(marking_t const & marking, std::string & out);
218 template <> void dump(marking_map const & marking_map, std::string & out);
219
220 class roster_t
221 {
222 public:
223 roster_t();
224 roster_t(roster_t const & other);
225 roster_t & operator=(roster_t const & other);
226 bool has_root() const;
227 bool has_node(file_path const & sp) const;
228 bool has_node(node_id nid) const;
229 bool is_root(node_id nid) const;
230 bool is_attached(node_id nid) const;
231 private:
232 node_t get_node_internal(file_path const & p) const;
233 public:
234 const_node_t get_node(file_path const & sp) const;
235 const_node_t get_node(node_id nid) const;
236 node_t get_node_for_update(file_path const & sp);
237 node_t get_node_for_update(node_id nid);
238 void get_name(node_id nid, file_path & sp) const;
239 void unshare(node_t & n, bool is_in_node_map = true);
240 void replace_node_id(node_id from, node_id to);
241
242 // editable_tree operations
243 node_id detach_node(file_path const & src);
244 void drop_detached_node(node_id nid);
245 node_id create_dir_node(node_id_source & nis);
246 void create_dir_node(node_id nid);
247 node_id create_file_node(file_id const & content,
248 node_id_source & nis);
249 void create_file_node(file_id const & content,
250 node_id nid);
251 void attach_node(node_id nid, file_path const & dst);
252 void attach_node(node_id nid, node_id parent, path_component name);
253 void apply_delta(file_path const & pth,
254 file_id const & old_id,
255 file_id const & new_id);
256 void clear_attr(file_path const & path,
257 attr_key const & key);
258 void set_attr(file_path const & path,
259 attr_key const & key,
260 attr_value const & val);
261 void set_attr(file_path const & path,
262 attr_key const & key,
263 std::pair<bool, attr_value> const & val);
264
265 // more direct, lower-level operations, for the use of roster_delta's
266 void detach_node(node_id nid);
267 void set_content(node_id nid,
268 file_id const & new_id);
269 void set_attr_unknown_to_dead_ok(node_id nid,
270 attr_key const & name,
271 std::pair<bool, attr_value> const & val);
272 void erase_attr(node_id nid,
273 attr_key const & name);
274
275 // misc.
276
277 bool get_attr(file_path const & pth,
278 attr_key const & key,
279 attr_value & val) const;
280
281 void get_file_details(node_id nid,
282 file_id & fid,
283 file_path & pth) const;
284
285 void extract_path_set(std::set<file_path> & paths) const;
286
all_nodes() const287 node_map const & all_nodes() const
288 {
289 return nodes;
290 }
291
292 bool operator==(roster_t const & other) const;
293
294 friend bool equal_shapes(roster_t const & a, roster_t const & b);
295
296 void check_sane(bool temp_nodes_ok=false) const;
297
298 // verify that this roster is sane, and corresponds to the given
299 // marking map
300 void check_sane_against(marking_map const & marks, bool temp_nodes_ok=false) const;
301
302 void print_to(data & dat,
303 marking_map const & mm,
304 bool print_local_parts) const;
305
306 void parse_from(basic_io::parser & pa,
307 marking_map & mm);
308
root() const309 dir_t const & root() const
310 {
311 return root_dir;
312 }
313
314 private:
315 //void do_deep_copy_from(roster_t const & other);
316 dir_t root_dir;
317 node_map nodes;
318 // This is set to a unique value when a roster is created or
319 // copied to/from another roster. If a node has the same version
320 // as the roster it's in, then it is not shared. Nodes with version
321 // zero are also not shared, since that means they haven't been added
322 // to a roster yet.
323 // A simple refcount isn't good enough, because everything that points
324 // to a node in question could also be shared.
325 mutable u32 cow_version;
326 // This requires some explanation. There is a particular kind of
327 // nonsensical behavior which we wish to discourage -- when a node is
328 // detached from some location, and then re-attached at that same location
329 // (or similarly, if a new node is created, then immediately deleted -- this
330 // is like the previous case, if you think of "does not exist" as a
331 // location). In particular, we _must_ error out if a cset attempts to do
332 // this, because it indicates that the cset had something non-normalized,
333 // like "rename a a" in it, and that is illegal. There are two options for
334 // detecting this. The more natural approach, perhaps, is to keep a chunk
335 // of state around while performing any particular operation (like cset
336 // application) for which we wish to detect these kinds of redundant
337 // computations. The other option is to keep this state directly within the
338 // roster, at all times. In the first case, we explicitly turn on checking
339 // when we want it; the the latter, we must explicitly turn _off_ checking
340 // when we _don't_ want it. We choose the latter, because it is more
341 // conservative --- perhaps it will turn out that it is _too_ conservative
342 // and causes problems, in which case we should probably switch to the
343 // former.
344 //
345 // The implementation itself uses the map old_locations. A node can be in
346 // the following states:
347 // -- attached, no entry in old_locations map
348 // -- detached, no entry in old_locations map
349 // -- create_dir_node, create_file_node put a node into this state
350 // -- a node in this state can be attached, anywhere, but may not be
351 // deleted.
352 // -- detached, an entry in old_locations map
353 // -- detach_node puts a node into this state
354 // -- a node in this state can be attached anywhere _except_ the
355 // (parent, basename) entry given in the map, or may be deleted.
356 std::map<node_id, std::pair<node_id, path_component> > old_locations;
357 template <typename T> friend void dump(T const & val, std::string & out);
358 };
359
360 struct temp_node_id_source
361 : public node_id_source
362 {
363 temp_node_id_source();
364 virtual node_id next();
365 node_id curr;
366 };
367
368 template <> void dump(roster_t const & val, std::string & out);
369
370 // adaptor class to enable cset application on rosters.
371 class editable_roster_base
372 : public editable_tree
373 {
374 public:
375 editable_roster_base(roster_t & r, node_id_source & nis);
376 virtual node_id detach_node(file_path const & src);
377 virtual void drop_detached_node(node_id nid);
378 virtual node_id create_dir_node();
379 virtual node_id create_file_node(file_id const & content);
380 virtual void attach_node(node_id nid, file_path const & dst);
381 virtual void apply_delta(file_path const & pth,
382 file_id const & old_id,
383 file_id const & new_id);
384 virtual void clear_attr(file_path const & path,
385 attr_key const & key);
386 virtual void set_attr(file_path const & path,
387 attr_key const & key,
388 attr_value const & val);
389 virtual void commit();
390 protected:
391 roster_t & r;
392 node_id_source & nis;
393 };
394
395
396 void
397 make_cset(roster_t const & from,
398 roster_t const & to,
399 cset & cs);
400
401 bool
402 equal_up_to_renumbering(roster_t const & a, marking_map const & a_markings,
403 roster_t const & b, marking_map const & b_markings);
404
405
406 // various (circular?) dependencies prevent inclusion of restrictions.hh
407 class node_restriction;
408
409 void
410 make_restricted_roster(roster_t const & from, roster_t const & to,
411 roster_t & restricted,
412 node_restriction const & mask);
413
414 void
415 select_nodes_modified_by_cset(cset const & cs,
416 roster_t const & old_roster,
417 roster_t const & new_roster,
418 std::set<node_id> & nodes_modified);
419
420 void
421 get_content_paths(roster_t const & roster,
422 std::map<file_id, file_path> & paths);
423
424 // These functions are for the use of things like 'update' or 'pluck', that
425 // need to construct fake rosters and/or markings in-memory, to achieve
426 // particular merge results.
427 void
428 mark_roster_with_no_parents(revision_id const & rid,
429 roster_t const & roster,
430 marking_map & markings);
431 void
432 mark_roster_with_one_parent(roster_t const & parent,
433 marking_map const & parent_markings,
434 revision_id const & child_rid,
435 roster_t const & child,
436 marking_map & child_markings);
437
438 void
439 mark_merge_roster(roster_t const & left_roster,
440 marking_map const & left_markings,
441 std::set<revision_id> const & left_uncommon_ancestors,
442 roster_t const & right_roster,
443 marking_map const & right_markings,
444 std::set<revision_id> const & right_uncommon_ancestors,
445 revision_id const & new_rid,
446 roster_t const & merge,
447 marking_map & new_markings);
448
449 // These functions are an internal interface between ancestry.cc and
450 // roster.cc; unless you know exactly what you're doing you probably want
451 // something else.
452
453 void
454 make_roster_for_merge(revision_id const & left_rid,
455 roster_t const & left_roster,
456 marking_map const & left_markings,
457 cset const & left_cs,
458 std::set<revision_id> const & left_uncommon_ancestors,
459
460 revision_id const & right_rid,
461 roster_t const & right_roster,
462 marking_map const & right_markings,
463 cset const & right_cs,
464 std::set<revision_id> const & right_uncommon_ancestors,
465
466 revision_id const & new_rid,
467 roster_t & new_roster,
468 marking_map & new_markings,
469 node_id_source & nis);
470
471 void
472 make_roster_for_nonmerge(cset const & cs,
473 revision_id const & new_rid,
474 roster_t & new_roster, marking_map & new_markings,
475 node_id_source & nis);
476
477
478 // This is for revisions that are being written to the db, only. It assigns
479 // permanent node ids.
480 void
481 make_roster_for_revision(database & db,
482 revision_t const & rev,
483 revision_id const & rid,
484 roster_t & result,
485 marking_map & marking);
486
487 // This is for revisions that are not necessarily going to be written to the
488 // db; you can specify your own node_id_source.
489 void
490 make_roster_for_revision(database & db,
491 node_id_source & nis,
492 revision_t const & rev,
493 revision_id const & rid,
494 roster_t & result,
495 marking_map & marking);
496
497 void
498 read_roster_and_marking(roster_data const & dat,
499 roster_t & ros,
500 marking_map & mm);
501
502 void
503 write_roster_and_marking(roster_t const & ros,
504 marking_map const & mm,
505 roster_data & dat);
506
507 void
508 write_manifest_of_roster(roster_t const & ros,
509 manifest_data & dat,
510 bool do_sanity_check = true);
511
512
513 void calculate_ident(roster_t const & ros,
514 manifest_id & ident,
515 bool do_sanity_check = true);
516
517 // for roster_delta
518
519 void append_with_escaped_quotes(std::string & collection,
520 std::string const & item);
521 void push_marking(std::string & contents,
522 bool is_file, const_marking_t const & mark,
523 int symbol_length);
524 void parse_marking(basic_io::parser & pa, marking_t & marking);
525
526 // Parent maps are used in a number of places to keep track of all the
527 // parent rosters of a given revision.
528
parent_id(parent_entry const & p)529 inline revision_id const & parent_id(parent_entry const & p)
530 {
531 return p.first;
532 }
533
parent_id(parent_map::const_iterator i)534 inline revision_id const & parent_id(parent_map::const_iterator i)
535 {
536 return i->first;
537 }
538
539 inline cached_roster const &
parent_cached_roster(parent_entry const & p)540 parent_cached_roster(parent_entry const & p)
541 {
542 return p.second;
543 }
544
545 inline cached_roster const &
parent_cached_roster(parent_map::const_iterator i)546 parent_cached_roster(parent_map::const_iterator i)
547 {
548 return i->second;
549 }
550
parent_roster(parent_entry const & p)551 inline roster_t const & parent_roster(parent_entry const & p)
552 {
553 return *(p.second.first);
554 }
555
parent_roster(parent_map::const_iterator i)556 inline roster_t const & parent_roster(parent_map::const_iterator i)
557 {
558 return *(i->second.first);
559 }
560
parent_marking(parent_entry const & p)561 inline marking_map const & parent_marking(parent_entry const & p)
562 {
563 return *(p.second.second);
564 }
565
parent_marking(parent_map::const_iterator i)566 inline marking_map const & parent_marking(parent_map::const_iterator i)
567 {
568 return *(i->second.second);
569 }
570
571 #endif
572
573 // Local Variables:
574 // mode: C++
575 // fill-column: 76
576 // c-file-style: "gnu"
577 // indent-tabs-mode: nil
578 // End:
579 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
580