VersionMap2.Mesa
Copyright Ó 1990 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on April 10, 1992 9:27 am PDT
DIRECTORY Atom, Basics, BasicTime, PFS, PFSNames, RefTab, Rope;
VersionMap2: CEDAR DEFINITIONS
IMPORTS PFSNames
= {
Basics
Extremely generic stuff.
ROPE: TYPE ~ Rope.ROPE;
LOR: TYPE ~ LIST OF ROPE;
Error: READONLY ERROR [error: PFS.ErrorDesc];
The generic error raised by procedures in this interface. This Error is PFS.Error.
Version Tuples
This section works up to defining a VersionTuple, which contains: a file name, a create date, and a version stamp.
Name: TYPE ~ PFS.PATH--that is absolute--;
NameList: TYPE ~ LIST OF Name;
nullName: Name ~ NIL;
NameMatch: PROC [x, p: Name] RETURNS [BOOL];
p=nullName => result is TRUE; otherwise, the standard PFS rules apply.
NameEqual: PROC [n1, n2: Name] RETURNS [BOOL]
~ INLINE {RETURN PFSNames.Equal[n1, n2]};
RtNameEqual: RefTab.EqualProc;
RtNameHash: RefTab.HashProc;
NameCompare: PROC [n1, n2: Name] RETURNS [Basics.Comparison];
A total ordering that is consistent with PFS's ordering.
NamePatternIsConstant: PROC [Name] RETURNS [BOOL];
NamePatternIsConstant[p] iff for all x1 and x2:
NameMatch[x1, p] AND NameMatch[x2, p] => NameEqual[x1, x2]
Created: TYPE ~ PFS.UniqueID;
nullCreated: Created ~ PFS.nullUniqueID;
CreatedFromGMT: PROC [gmt: BasicTime.GMT] RETURNS [Created]
~ INLINE {RETURN [[egmt: [gmt, 0] ]]};
GMTFromCreated: PROC [c: Created] RETURNS [GMTPlus]
Returns [c.egmt.gmt, c#CreatedFromGMT[c.egmt.gmt]].
~ INLINE {RETURN [[c.egmt.gmt, c.egmt.usecs#0 OR c.host#[0, 0] ]]};
GMTPlus: TYPE ~ RECORD [gmt: BasicTime.GMT, other: BOOL];
CreatedMatch: PROC [x, p: Created] RETURNS [BOOL]
~ INLINE {RETURN [p=nullCreated OR x=p]};
CreatedCompare: PROC [c1, c2: Created] RETURNS [Basics.Comparison];
VersionStamp: TYPE ~ ARRAY [0 .. 1] OF CARD --= MobDefs.VersionStamp--;
nullStamp: VersionStamp ~ [0, 0];
StampMatch: PROC [x, p: VersionStamp] RETURNS [BOOL]
~ INLINE {RETURN [p=nullStamp OR x=p]};
StampCompare: PROC [s1, s2: VersionStamp] RETURNS [Basics.Comparison];
nullTuple: VersionTuple ~ [];
VersionTuple: TYPE ~ RECORD [
name: Name ← nullName,
created: Created ← nullCreated,
stamp: VersionStamp ← nullStamp];
TMatches: PROC [t, pattern: VersionTuple] RETURNS [BOOL];
TMatches[t, p] iff NameMatch[t.name, p.name], CreatedMatch[t.created, p.created], and StampMatch[t.stamp, p.stamp].
TEqual: PROC [t1, t2: VersionTuple] RETURNS [BOOL];
TEqual[t1, t2] iff (TPCompare[t1, t2] = equal).
TupleCompare: PROC [t1, t2: VersionTuple] RETURNS [Basics.Comparison];
VersionTuples are totally ordered.
ShortNameToPattern: PROC [ROPE] RETURNS [VersionTuple];
Returns one that matches every file having the given short name.
LastComponentToPattern: PROC [PFSNames.Component] RETURNS [VersionTuple];
Returns one that matches every file having the given last component.
PatternIsConstant: PROC [VersionTuple] RETURNS [BOOL];
PatternIsConstant[p] iff for all x1 and x2:
TMatches[x1, p] AND TMatches[x2, p] => TEqual[x1, x2]
Version Map Basics
A Map represents a relation (a set of tuples). The relation may be functional, which means no two tuples have equal name fields.
Map: TYPE ~ RECORD [class: MapClass, data: REF ANYNIL];
nullMap: Map ~ [NIL];
MapList: TYPE ~ LIST OF Map;
RefMap: TYPE ~ REF Map;
Refify: PROC [map: Map] RETURNS [RefMap]
~ INLINE {RETURN [NEW[Map ← map]]};
DeRefify: PROC [arg: REF ANY] RETURNS [RefMap];
cantCode: READONLY ATOM;
Functional: PROC [map: Map] RETURNS [BOOL]
~ INLINE {RETURN map.class.Functional[map]};
MMutability: PROC [map: Map] RETURNS [Mutability]
~ INLINE {RETURN map.class.MMutability[map]};
Mutability: TYPE ~ {constant, readonly, writable};
readonly means the map contents may change, but not due to actions on this Map.
Querying Version Maps
Has: PROC [map: Map, elt: VersionTuple] RETURNS [BOOL]
Does the given map contain the given tuple?
~ INLINE {RETURN map.class.Has[map, elt]};
Scan: PROC [map: Map, Consume: TupleConsumer, inOrder: BOOL, filters: MapList ← NIL] RETURNS [MaybeTuple]
Passes to Consume those tuples of map that pass the intersection of the filters. If and when Consume[t] returns TRUE, the enumeration is stopped and [TRUE, t] is returned; otherwise [FALSE, nullTuple] is returned. Tuples are passed in TCompare order if inOrder. Consume may call other procedures on the map; if the map is changed during enumeration, there are no guarantess about whether tuples added or removed are enumerated.
~ INLINE {RETURN map.class.Scan[map, Consume, inOrder, PatternFactorMapList[filters]]};
TupleConsumer: TYPE ~ PROC [VersionTuple] RETURNS [BOOL];
MaybeTuple: TYPE ~ RECORD [found: BOOL, it: VersionTuple];
(NOT found) => (it = nullTuple).
noMaybe: MaybeTuple ~ [FALSE, nullTuple];
AlwaysAccept: TupleConsumer;
Always returns TRUE.
FactoredScan: PROC [map: Map, Consume: TupleConsumer, inOrder: BOOL, pfml: PatternFactoredMapList] RETURNS [MaybeTuple]
Equivalent to Scan[map, Consume, inOrder, PatternUnfactorMapList[pfml]].
~ INLINE {RETURN map.class.Scan[map, Consume, inOrder, pfml]};
AllHave: PROC [maplist: MapList, t: VersionTuple] RETURNS [BOOL];
TRUE iff for all m in maplist: Has[m, t].
OneHas: PROC [maplist: MapList, t: VersionTuple] RETURNS [BOOL];
TRUE iff there exists an m in maplist such that Has[m, t].
PfmlHas: PROC [pfml: PatternFactoredMapList, t: VersionTuple] RETURNS [BOOL];
Equivalent to AllHave[PatternUnfactorMapList[pfml], t].
CreateGenerator: PROC [map: Map, inOrder: BOOL, filters: MapList ← NIL] RETURNS [Generator]
The passive form of Scan --- return a closure that can generate the elements under client control. Use of generators is covered in a later section.
~ INLINE {RETURN map.class.CreateGenerator[map, inOrder, PatternFactorMapList[filters]]};
FactoredCreateGenerator: PROC [map: Map, inOrder: BOOL, pfml: PatternFactoredMapList] RETURNS [Generator]
~ INLINE {RETURN map.class.CreateGenerator[map, inOrder, pfml]};
ScanMatches: PROC [map: Map, Consume: TupleConsumer, inOrder: BOOL, pattern: VersionTuple] RETURNS [MaybeTuple]
See CreateMatcher for details about matching.
~ INLINE {RETURN map.class.Scan[map, Consume, inOrder, [pattern]]};
GenerateMatches: PROC [map: Map, inOrder: BOOL, pattern: VersionTuple] RETURNS [Generator]
~ INLINE {RETURN map.class.CreateGenerator[map, inOrder, [pattern]]};
TheElt: PROC [map: Map] RETURNS [VersionTuple];
Raises Error[client, $NotUnique] if map.Size[] # 1; otherwise returns the one and only tuple in the map.
AnElt: PROC [map: Map] RETURNS [MaybeTuple];
IF map.Size[]=0
THEN RETURN [[FALSE, nullTuple]]
ELSE RETURN [[TRUE, one of the elements of map]].
LookupStamp: PROC [map: Map, stamp: VersionStamp] RETURNS [MaybeTuple];
Returns AnElt[map.Intersection[CreateMatcher[stamp: stamp]]].
LookupCreated: PROC [map: Map, created: Created] RETURNS [MaybeTuple];
Returns AnElt[map.Intersection[CreateMatcher[created: created]]].
LookupShortName: PROC [map: Map, shortName: ROPE] RETURNS [MaybeTuple];
Returns AnElt[map.Intersection[CreateMatcher[name: ShortNameToPattern[shortName]]]].
Size: PROC [map: Map, limit: INTINT.LAST] RETURNS [INT]
Result is anything in [MIN[size, limit] .. size]. This lets clients that don't care about the difference between 2 and infinity, and implementations whose cost is proportional to the answer, get along without much waste.
~ INLINE {RETURN map.class.Size[map, limit]};
Empty: PROC [map: Map] RETURNS [BOOL]
~ INLINE {RETURN [Size[map, 1]=0]};
Modifying Version Maps
AddTuple: PROC [map: Map, t: VersionTuple, if: IfHads ← always] RETURNS [had: Hads]
Possibly adds a tuple t into map, depending on whether it's OK to replace the pre-existing tuple with the same name (if there is one). For functional maps, if controls whether replacement is done and had reveals some information about the state of map before this operation; for non-functional maps if is ignored (the addition is always done) and there are no guarantees about had (it may even be unitialized).
~ INLINE {RETURN map.class.AddTuple[map, t, if]};
OldCase: TYPE ~ {same, createdDifferent, stampDifferent, bothDifferent, new};
The OldCase for possibly adding a tuple t to a map m is:
new if no u in m has the same name as t,
same if t is in m,
createdDifferent if some u in m has t's name and stamp but a different created,
stampDifferent if some u in m has t's name and created but a different stamp,
bothDifferent if some u in m has t's name and different created and stamp.
InterestingOldCase: TYPE ~ OldCase[createdDifferent .. new];
When t is already in m, there's no question about whether to add t to m.
IfHads: TYPE ~ PACKED ARRAY InterestingOldCase OF --add:--BOOL;
Such an array tells whether to add a tuple to a map, according to the case.
always: IfHads ~ ALL[TRUE];
ifDifferent: IfHads ~ [TRUE, TRUE, TRUE, FALSE];
ifNew: IfHads ~ [FALSE, FALSE, FALSE, TRUE];
Some specific arrays of interest.
Hads: TYPE ~ PACKED ARRAY DependentField OF Had;
DependentField: TYPE ~ {created, stamp};
Had: TYPE ~ {same, different, none};
A Had partially reveals a state of a map m with respect to a tuple t and a dependent field f:
none means u.name # t.name for every u in m,
same means u.name = t.name and u.f = t.f for some u in m, and
different means u.name = t.name and u.f # t.f for some u in m.
AddMap: PROC [map, other: Map, if: IfHads ← always] RETURNS [some: HadSets]
Adds the tuples of other into map, possibly replacing pre-existing tuples with equal names as necessary. For functional maps, if controls whether replaceent is done and some reveals some information about the state of map before this operation; for non-functional maps, if is ignored and there are no guarantees about some. other must be able to enumerate.
~ INLINE {RETURN map.class.AddMap[map, other, if]};
HadSet: TYPE ~ PACKED ARRAY Had OF BOOL;
HadSets: TYPE ~ ARRAY DependentField OF HadSet ← ALL[ALL[FALSE]];
A HadSets summarizes a relation between two Maps (map and other). Specifically, hs[f][h] is TRUE iff there exists some tuple t in other such that the Had for map with respect to t and f is h.
RemTuple: PROC [map: Map, t: VersionTuple] RETURNS [had: Hads]
Removes a tuple from a writable map, if that tuple was in the map. The previous state of the map is partially revealed by had if the map is functional; no guarantees about had for non-functional maps.
~ INLINE {RETURN map.class.RemTuple[map, t]};
RemMap: PROC [map, other: Map] RETURNS [some: HadSets]
Removes the tuples of other from map. The previous state of the map is partially revealed by had if the map is functional; no guarantees about had for non-functional maps. other must be able to enumerate.
~ INLINE {RETURN map.class.RemMap[map, other]};
RemFilters: PROC [map: Map, filters: MapList];
Like RemMap[map, intersection of filters], except that no information is returned, and thus the filters need not be able to enumerate.
Boolean Combinations of Version Maps
Negate: PROC [map: Map] RETURNS [Map]
Produces a map that can test membership but may not be able to enumerate. Argument and result cannot both be functional.
~ INLINE {RETURN map.class.Negate[map]};
IsNegation: PROC [map: Map] RETURNS [MaybeMap]
IsNegation[Negate[Negate[x]]] might return [FALSE, nullMap].
~ INLINE {RETURN map.class.IsNegation[map]};
MaybeMap: TYPE ~ RECORD [found: BOOL, it: Map];
(NOT found) => (it = nullMap).
Intersect: PROC [map, other: Map] RETURNS [Map]
Returns a map that contain exactly those tuples contained in both given maps. Implements enumeration by enumerating map and testing membership in other.
~ INLINE {RETURN map.class.Intersect[map, other]};
IsIntersection: PROC [map: Map] RETURNS [Maybe2Maps]
IsIntersection[x] does not imply that x was made with Intersect.
~ INLINE {RETURN map.class.IsIntersection[map]};
Maybe2Maps: TYPE ~ RECORD [found: BOOL, map, other: Map];
(NOT found) => (map = other = nullMap).
Union: PROC [map, other: Map, test: UnionTest ← other, ordered: BOOLFALSE] RETURNS [Map]
test=disjoint => map.Intersect[other].Emtpy
ordered => TBCompare[t, u] <= equal for all t in map, u in other.
test=map => enumeration implemented by enumerating map, enumerating other and testing membership in map.
test=other => enumeration implemented by enumerating other, enumerating map and testing membership in other.
~ INLINE {RETURN map.class.Union[map, other, test, ordered]};
UnionTest: TYPE ~ {disjoint, map, other};
IsUnion: PROC [map: Map] RETURNS [Maybe2Maps]
~ INLINE {RETURN map.class.IsUnion[map]};
Difference: PROC [map, other: Map] RETURNS [Map]
Enumeration implemented by enumerating map and testing membership in other.
~ INLINE {RETURN map.class.Difference[map, other]};
IsDifference: PROC [map: Map] RETURNS [Maybe2Maps]
~ INLINE {RETURN map.class.IsDifference[map]};
SymmetricDifference: PROC [map, other: Map] RETURNS [Map]
Result can enumerate iff both args can; quickly only if both args can quickly test membership.
~ INLINE {RETURN map.class.SymmetricDifference[map, other]};
IsSymmetricDifference: PROC [map: Map] RETURNS [Maybe2Maps]
~ INLINE {RETURN map.class.IsSymmetricDifference[map]};
Various Interesting Maps
anEmpty: READONLY Map;
A constant map with no elements.
aFull: READONLY Map;
A constant map that contains every VersionTuple.
CreateSingleton: PROC [VersionTuple] RETURNS [Map];
MapIsSingleton: PROC [Map] RETURNS [MaybeTuple];
If map.Size[]=1, returns [TRUE, the T that map has], otherwise [FALSE, nullTuple].
CreateVariableMap: PROC RETURNS [Map];
The result is functional, writable, and initially empty.
MapFromFile: PROC [fileName: Name, created: Created, assumeImmutable: BOOL] RETURNS [Map];
Reads map contents from a file; assumeImmutable indicates whether MapFromFile can assume the file's contents will not change. MapFromFile always returns a constant map. Thus, if assumeImmutable, the returned map can use a RopeFile on the given file; otherwise, the entire file contents must be copied (into memory or another file).
MapIsFromFile: PROC [Map] RETURNS [MaybeFromFile];
MaybeFromFile: TYPE ~ RECORD [found: BOOL, fileName: Name, created: Created, assumeImmutable: BOOL];
(NOT found) => (it = nullName).
StoreToFile: PROC [map: Map, fileName: Name];
CreateDirectoriesMap: PROC [dirs: Map, cache, hints: Map ← nullMap] RETURNS [Map];
A `directories map' contains all the files in a set of directories. The directories are the members of dirs (created and stamp unimportant). If given, hints and cache are consulted before file contents to determine version stamps. If cache is given, stamps read from files are stored there.
Pattern Matching Maps.
CreateMatcher: PROC [pattern: VersionTuple] RETURNS [Map];
Creates a Map that can test membership, but cannot enumerate (unless the pattern is constant). It contains every tuple t such that TMatches[t, pattern].
PatternFactoredMap: TYPE ~ RECORD [
pattern: VersionTuple,
stampInd, general: Map
];
A factored representation for the intersection of CreateMatcher[pattern], stampInd, and general. Neither stampInd nor general is NIL; aFull is used to say `no more constraints'. Membership in stampInd (`stamp independent') does not depend on a tuple's stamp; that is, for all n, c, s1, and s2: <n, c, s1> is in stampInd if and only if <n, c, s2> is in stampInd.
PatternFactorMap: PROC [map: Map] RETURNS [PatternFactoredMap]
Any map that can yield useful information without doing a lot of work is expected to do so.
~ INLINE {RETURN map.class.PatternFactorMap[map]};
PatternFactoredMapList: TYPE ~ RECORD [
pattern: VersionTuple ← nullTuple,
stampInd, general: MapList ← NIL
];
Represents the intersection of CreateMatcher[pattern], the members of stampInd, and the members of general. The members of stampInd do not care about the version stamps of tuples. Setting stampInd=listOfEmpty is a particularly obvious way to indicate the empty intersection.
listOfEmpty: READONLY MapList; --LIST[anEmpty]
PatternFactorMapList: PROC [MapList] RETURNS [PatternFactoredMapList];
If empty intersection detected, stampInd=listOfEmpty.
PatternUnfactorMapList: PROC [PatternFactoredMapList] RETURNS [MapList];
The inverse of PatternFactorMapList.
PfmlAddMap: PROC [PatternFactoredMapList, Map] RETURNS [PatternFactoredMapList];
Result represents the intersection of the arguments.
CreateSetMatcher: PROC [prefix: VersionTuple, suffixes: LOR] RETURNS [Map];
Returns a constant map that matches every tuple whose name is the concatenation of prefix.name and one of the suffixes.
Generators
Generators produce VersionTuples one at a time.
Generator: TYPE ~ REF GeneratorRep;
GeneratorRep: TYPE ~ RECORD [
Next: PROC [Generator] RETURNS [VersionTuple],
Returns nullTuple when there are no more.
Close: PROC [Generator],
Tells the generator that no more of its values will be needed.
data: REF ANYNIL];
GeneratorUnion: PROC [g1, g2: Generator, sort: BOOL] RETURNS [Generator];
Result generates elements of both generators, which must be disjoint. Result does not support concurrent calls. If sort, both generators are assumed to generate in order, and the result alternates between them to preserve order. Closes g1 and g2 when closed.
GeneratorSort: PROC [Generator] RETURNS [Generator];
Result generates the same elements as the argument, but in order. Result does not support concurrent calls. Closes arg when closed.
FilterGenerator: PROC [Generator, MapList] RETURNS [Generator];
Returns a more restricted generator. Result does not support concurrent calls. Closes given generator when closed.
TheGeneratee: PROC [gen: Generator] RETURNS [VersionTuple];
If the generator has exactly one more tuple to generate, that one is returned; otherwise, raises Error[client, $NotUnique].
GeneratorQuaTuple: PROC [gen: Generator] RETURNS [MaybeTuple];
If the generator has exactly one more tuple to generate, returns [TRUE, the tuple]; otherwise, returns [FALSE, nullTuple].
Implementing Maps
Only implementors of map classes need to know this.
MapClass: TYPE ~ REF MapClassRep;
MapClassRep: TYPE ~ RECORD [
Functional: PROC [map: Map] RETURNS [BOOL],
MMutability: PROC [map: Map] RETURNS [Mutability],
Has: PROC [map: Map, elt: VersionTuple] RETURNS [BOOL] ← NIL,
Scan: PROC [map: Map, Consume: TupleConsumer, inOrder: BOOL, pfml: PatternFactoredMapList] RETURNS [MaybeTuple] ← NIL,
CreateGenerator: PROC [map: Map, inOrder: BOOL, pfml: PatternFactoredMapList] RETURNS [Generator] ← NIL,
Size: PROC [map: Map, limit: INT] RETURNS [INT] ← NIL,
PatternFactorMap: PROC [map: Map] RETURNS [PatternFactoredMap] ← NIL,
AddTuple: PROC [map: Map, t: VersionTuple, if: IfHads] RETURNS [had: Hads] ← NIL,
AddMap: PROC [map, other: Map, if: IfHads] RETURNS [some: HadSets] ← NIL,
RemTuple: PROC [map: Map, t: VersionTuple] RETURNS [had: Hads] ← NIL,
RemMap: PROC [map, other: Map] RETURNS [some: HadSets] ← NIL,
Negate: PROC [map: Map] RETURNS [Map] ← NIL,
IsNegation: PROC [map: Map] RETURNS [MaybeMap] ← NIL,
Intersect: PROC [map, other: Map] RETURNS [Map] ← NIL,
IsIntersection: PROC [map: Map] RETURNS [Maybe2Maps] ← NIL,
Union: PROC [map, other: Map, test: UnionTest, ordered: BOOL] RETURNS [Map] ← NIL,
IsUnion: PROC [map: Map] RETURNS [Maybe2Maps] ← NIL,
Difference: PROC [map, other: Map] RETURNS [Map] ← NIL,
IsDifference: PROC [map: Map] RETURNS [Maybe2Maps] ← NIL,
SymmetricDifference: PROC [map, other: Map] RETURNS [Map] ← NIL,
IsSymmetricDifference: PROC [map: Map] RETURNS [Maybe2Maps] ← NIL,
other: Atom.PropList ← NIL,
data: REF ANY];
}.