Merge branch 'test' of https://github.com/urbit/urbit into test

This commit is contained in:
Anton Dyudin 2014-09-09 14:33:51 -07:00 committed by Anton Dyudin
commit 56f03cc709
2 changed files with 122 additions and 163 deletions

View File

@ -1787,7 +1787,7 @@
%- aeon-to-tako
let
?^(r.mun ~ [~ [t.yak (forge-nori yak)]])
::?> ?=(^ hit) ?^(r.mun ~ [~ i.hit]) :: what do?? need [@da nori]
::?> ?=(^ hit) ?^(r.mun ~ [~ i.hit]) :: what do?? need [@da nori]
(query(ank ank:(descend-path:(zu ank) r.mun)) p.mun)
::
++ read-at-aeon :: read-at-aeon:ze

View File

@ -49,8 +49,6 @@ to clay is stored in this state.
`fat` is the set of domestic servers. This stores all the information that is
specfic to a particular ship on this pier. The keys to this map are the ships
on the current pier.
`hoy` is the set of foreign servers that we know anything about. This stores
all the information that is specific to a particular foreign ship. The keys to
this map are all the ships whose filesystems we have attempted to access
through clay.
@ -954,9 +952,8 @@ actually try to read the data.
!!
=+ ezy=?~(ref ~ (~(get by haw.u.ref) mun))
?^ ezy ezy
=+ nao=(~(aeon ze lim dom ran) q.mun)
:: ~& [%aver-mun nao [%from syd lim q.mun]]
?~(nao ~ [~ (~(avid ze lim dom ran) u.nao mun)])
=+ nao=(~(case-to-aeon ze lim dom ran) q.mun)
?~(nao ~ [~ (~(read-at-aeon ze lim dom ran) u.nao mun)])
```
We check immediately that we're not requesting the `rang` for any time other
@ -971,21 +968,21 @@ specified by the given case, and then we try to get the data there.
Here, we jump into `arvo/zuse.hoon`, which is where much of the algorithmic
code is stored, as opposed to the clay interface, which is stored in
`arvo/clay.hoon`. We examine `++aeon:ze`.
`arvo/clay.hoon`. We examine `++case-to-aeon:ze`.
```
++ aeon :: aeon:ze
++ case-to-aeon :: case-to-aeon:ze
|= lok=case :: act count through
^- (unit ,@ud)
^- (unit aeon)
?- -.lok
%da
?: (gth p.lok lim) ~
|- ^- (unit ,@ud)
|- ^- (unit aeon)
?: =(0 let) [~ 0] :: avoid underflow
?: %+ gte p.lok
=< t
%- ~(got by hut)
%- ~(got by hit)
%- tako-to-yaki
%- aeon-to-tako
let
[~ let]
$(let (dec let))
@ -998,7 +995,7 @@ code is stored, as opposed to the clay interface, which is stored in
We handle each type of `case` differently. The latter two types are easy.
If we're requesting a revision by label, then we simply look up the requested
label in `lab` from the given dome. If it exists, that is our number; else we
label in `lab` from the given dome. If it exists, that is our aeon; else we
produce null, indicating the requested revision does not yet exist.
If we're requesting a revision by number, we check if we've yet reached that
@ -1010,30 +1007,30 @@ scan backwards until we find the first revision committed before that date, and
we produce that. If we requested a date before any revisions were committed,
we produce `0`.
Assuming we got a valid version number, `++aver` calls `++avid:ze`, which reads
the requested data at the given revision.
Assuming we got a valid version number, `++aver` calls `++read-at-aeon:ze`,
which reads the requested data at the given revision.
```
++ avid :: avid:ze
|= [oan=@ud mun=mood] :: seek and read
++ read-at-aeon :: read-at-aeon:ze
|= [oan=aeon mun=mood] :: seek and read
^- (unit)
?: &(?=(%w p.mun) !?=(%ud -.q.mun)) :: NB only for speed
?^(r.mun ~ [~ oan])
(auto:(argo oan) mun)
(read:(rewind oan) mun)
```
If we're requesting the revision number with a case other than by number, then
we go ahead and just produce the number we were given. Otherwise, we call
`++argo` to rewind our state to the given revision, and then we call `++auto`
`++rewind` to rewind our state to the given revision, and then we call `++read`
to get the requested information.
```
++ argo :: argo:ze
|= oan=@ud :: rewind to aeon
++ rewind :: rewind:ze
|= oan=aeon :: rewind to aeon
^+ +>
?: =(let oan) +>
?: (gth oan let) !! :: don't have this version
+>(ank (azel q:(~(got by hut) (~(got by hit) oan))), let oan)
?: (gth oan let) !! :: don't have version
+>(ank (checkout-ankh q:(tako-to-yaki (aeon-to-tako oan))), let oan)
```
If we're already at the requested version, we do nothing. If we're requesting
@ -1041,11 +1038,11 @@ a version later than our head, we are unable to comply.
Otherwise, we get the hash of the commit at the number, and from that we get
the commit itself (the yaki), which has the map of path to lobe that represents
a version of the filesystem. We call `++azel` to checkout the commit, and we
a version of the filesystem. We call `++checkout-ankh` to checkout the commit, and we
replace `ank` in our context with the result.
```
++ azel :: azel:ze
++ checkout-ankh :: checkout-ankh:ze
|= hat=(map path lobe) :: checkout commit
^- ankh
%- cosh
@ -1054,7 +1051,7 @@ replace `ank` in our context with the result.
^- ankh
%- cosh
?~ pat
=+ zar=(zaul bar)
=+ zar=(lobe-to-noun bar)
ank(q [~ (sham zar) zar])
=+ nak=(~(get by r.ank) i.pat)
%= ank
@ -1064,16 +1061,16 @@ replace `ank` in our context with the result.
```
Twice we call `++cosh`, which hashes a commit, updating `p` in an `ankh`.
Let's jump into that algorithm before we describe `++azel`.
Let's jump into that algorithm before we describe `++checkout-ankh`.
```
++ cosh :: locally rehash
|= ank=ankh
ank(p dash:(zu ank))
|= ank=ankh :: NB v/unix.c
ank(p rehash:(zu ank))
```
We simply replace `p` in the hash with the `cash` we get from a call to
`++dash:zu`.
`++rehash:zu`.
```
++ zu !: :: filesystem
@ -1081,7 +1078,7 @@ We simply replace `p` in the hash with the `cash` we get from a call to
=| myz=(list ,[p=path q=miso]) :: changes in reverse
=| ram=path :: reverse path into
|%
++ dash :: local rehash
++ rehash :: local rehash
^- cash
%+ mix ?~(q.ank 0 p.u.q.ank)
=+ axe=1
@ -1098,43 +1095,40 @@ We simply replace `p` in the hash with the `cash` we get from a call to
checkout of the filesystem and access the actual data inside it. One of the
things we can do with it is to create a recursive hash of the node.
In `++dash`, if this node is a file, then we xor the remainder of the hash with
the hash of the contents of the file. The remainder of the hash is `0` if we
have no children, else we descend into our children. Basically, we do a half
SHA-256 of the xor of the axis of this child and the half SHA-256 of the xor of
the name of the child and the hash of the child. This is done for each child
and all the results are xored together.
In `++rehash`, if this node is a file, then we xor the remainder of the hash
with the hash of the contents of the file. The remainder of the hash is `0` if
we have no children, else we descend into our children. Basically, we do a
half SHA-256 of the xor of the axis of this child and the half SHA-256 of the
xor of the name of the child and the hash of the child. This is done for each
child and all the results are xored together.
Now we return to our discussion of `++azel`.
Now we return to our discussion of `++checkout-ankh`.
We fold over every path in this version of the filesystem and create a great
ankh out of them. First, we call `++zaul` to get the raw data referred to be
each lobe.
ankh out of them. First, we call `++lobe-to-noun` to get the raw data referred
to be each lobe.
```
++ zaul :: grab blob
++ lobe-to-noun :: grab blob
|= p=lobe :: ^- *
%- zaru
(zaal p)
%- blob-to-noun
(lobe-to-blob p)
```
This converts a lobe into the raw data it refers to by first getting the blob
with `++zaal` and converting that into data with `++zaru`.
with `++lobe-to-blob` and converting that into data with `++blob-to-noun`.
```
++ zaal :: grab blob
|= p=lobe :: (raw)
^- blob
(~(got by lat) p)
++ lobe-to-blob ~(got by lat) :: grab blob
```
This just grabs the blob that the lobe refers to.
```
++ zaru :: grab blob
++ blob-to-noun :: grab blob
|= p=blob
?- -.p
%delta (lump r.p (zaul q.p))
%delta (lump r.p (lobe-to-noun q.p))
%direct q.p
%indirect q.p
==
@ -1211,10 +1205,10 @@ are what we expect them to be (`p.i.rug`), crashing on failure. If they're
good, then we append the new lines in `q.i.rug` onto `war`.
And that's really it. List merges are pretty easy. Anyway, if you recall, we
were discussing `++azel`.
were discussing `++checkout-ankh`.
```
++ azel :: azel:ze
++ checkout-ankh :: checkout-ankh:ze
|= hat=(map path lobe) :: checkout commit
^- ankh
%- cosh
@ -1223,7 +1217,7 @@ were discussing `++azel`.
^- ankh
%- cosh
?~ pat
=+ zar=(zaul bar)
=+ zar=(lobe-to-noun bar)
ank(q [~ (sham zar) zar])
=+ nak=(~(get by r.ank) i.pat)
%= ank
@ -1235,7 +1229,7 @@ were discussing `++azel`.
If the path is null, then we calculate `zar`, the raw data at the path `pat` in
this version. We produce the given ankh with the correct data.
Otherwise, we try to get the child we're looking at from our parent `ankh`. If
Otherwise, we try to get the child we're looking at from our parent ankh. If
it's already been created, this succeeds; otherwise, we simply create a default
blank ankh. We place ourselves in our parent after recursively computing our
children.
@ -1271,7 +1265,7 @@ In the recursion, we our path is `/english` and our ankh is again blank. We
try to get the `english` child of our ankh, but this of course fails. Thus,
we update our blank `/greeting` ankh with a child `english` produced by recursing.
Now our path is null, so we call `++zaul` to get the actual data, and we place
Now our path is null, so we call `++lobe-to-noun` to get the actual data, and we place
it in the brand-new ankh.
Next, we process `/greeting/russian/short`. Since our path is not null, we try
@ -1310,54 +1304,55 @@ depending on what order the paths come in, but the resulting tree is
independent of order.
At any rate, we were talking about something important, weren't we? If you
recall, that concludes our discussion of `++argo`, which was called from
`++avid`. In summary, `++argo` returns a context in which our current state is
(very nearly) as it was when the specified version of the desk was the head.
This allows `++avid` to call `++auto` to read the requested information.
recall, that concludes our discussion of `++rewind`, which was called from
`++read-at-aeon`. In summary, `++rewind` returns a context in which our
current state is (very nearly) as it was when the specified version of the desk
was the head. This allows `++read-at-aeon` to call `++read` to read the
requested information.
```
++ auto :: auto:ze
++ read :: read:ze
|= mun=mood :: read at point
^- (unit)
?: ?=(%v p.mun)
[~ `dome`+<+<.auto]
[~ `dome`+<+<.read]
?: &(?=(%w p.mun) !?=(%ud -.q.mun))
?^(r.mun ~ [~ let])
?: ?=(%w p.mun)
=+ ^= yak
%- ~(got by hut)
%- ~(got by hit)
%- tako-to-yaki
%- aeon-to-tako
let
?^(r.mun ~ [~ [t.yak (azul yak)]])
::?> ?=(^ hit) ?^(r.mun ~ [~ i.hit]) :: what do?? need [@da nori]
(amor(ank ank:(deny:(zu ank) r.mun)) p.mun)
?^(r.mun ~ [~ [t.yak (forge-nori yak)]])
::?> ?=(^ hit) ?^(r.mun ~ [~ i.hit]) :: what do?? need [@da nori]
(query(ank ank:(descend-path:(zu ank) r.mun)) p.mun)
```
If we're requesting the dome, then we just return that immediately.
If we're requesting the revision number of the desk and we're not requesting it
by number, then we just return the current number of this desk. Note of course
that this was really already handled in `++avid`.
that this was really already handled in `++read-at-aeon`.
If we're requesting a `%w` with a specific revision number, then we do
something or other with the commit there. It's kind of weird, and it doesn't
seem to work, so we'll ignore this case.
Otherwise, we descend into the ankh tree with `++deny:zu` to the given path,
and then we handle specific request in `++amor`.
Otherwise, we descend into the ankh tree to the given path with
`++descend-path:zu`, and then we handle specific request in `++query`.
```
++ deny :: descend recursively
++ descend-path :: descend recursively
|= way=path
^+ +>
?~(way +> $(way t.way, +> (dent i.way)))
?~(way +> $(way t.way, +> (descend i.way)))
```
This is simple recursion down into the ankh tree. `++dent` descends one level,
so this will eventually get us down to the path we want.
This is simple recursion down into the ankh tree. `++descend` descends one
level, so this will eventually get us down to the path we want.
```
++ dent :: descend
++ descend :: descend
|= lol=@ta
^+ +>
=+ you=(~(get by r.ank) lol)
@ -1372,24 +1367,25 @@ Once we've decscended to the correct level, we need to actually deal with the
request.
```
++ amor :: amor:ze
++ query :: query:ze
|= ren=?(%u %v %x %y %z) :: endpoint query
^- (unit ,*)
?- ren
%u [~ `rang`+<+>.amor]
%v [~ `dome`+<+<.amor]
%u [~ `rang`+<+>.query]
%v [~ `dome`+<+<.query]
%x ?~(q.ank ~ [~ q.u.q.ank])
%y [~ ache]
%y [~ as-arch]
%z [~ ank]
==
```
Now that everything's set up, it's really easy. If they're requesting the rang
, dome, or ankh, we give it to them. If the contents of a file, we give it to
them if it is in fact a file. If the `arch`, then we calculate it with `++ache`.
Now that everything's set up, it's really easy. If they're requesting the
rang, dome, or ankh, we give it to them. If the contents of a file, we give it
to them if it is in fact a file. If the `arch`, then we calculate it with
`++as-arch`.
```
++ ache :: ache:ze
++ as-arch :: as-arch:ze
^- arch :: arch report
:+ p.ank
?~(q.ank ~ [~ p.u.q.ank])
@ -1411,19 +1407,19 @@ rave. If the head was `&`, then it was a single request, so we handled it
above. If `|`, then we handle it with the following code.
```
=+ nab=(~(aeon ze lim dom ran) p.p.rav)
=+ nab=(~(case-to-aeon ze lim dom ran) p.p.rav)
?~ nab
?> =(~ (~(aeon ze lim dom ran) q.p.rav))
?> =(~ (~(case-to-aeon ze lim dom ran) q.p.rav))
(duce hen (rive rav))
=+ huy=(~(aeon ze lim dom ran) q.p.rav)
=+ huy=(~(case-to-aeon ze lim dom ran) q.p.rav)
?: &(?=(^ huy) |((lth u.huy u.nab) &(=(0 u.huy) =(0 u.nab))))
(blub hen)
=+ top=?~(huy let.dom u.huy)
=+ sar=(~(apax ze lim dom ran) u.nab r.p.rav)
=+ ear=(~(apax ze lim dom ran) top r.p.rav)
=+ sar=(~(lobes-at-path ze lim dom ran) u.nab r.p.rav)
=+ ear=(~(lobes-at-path ze lim dom ran) top r.p.rav)
=. +>.$
?: =(sar ear) +>.$
=+ fud=(~(gack ze lim dom ran) u.nab top)
=+ fud=(~(make-nako ze lim dom ran) u.nab top)
(bleb hen u.nab fud)
?^ huy
(blub hen)
@ -1433,9 +1429,9 @@ above. If `|`, then we handle it with the following code.
==
```
Recall that `++aeon:ze` produces the revision number that a case corresponds
to, if it corresponds to any. If it doesn't yet correspond to a revision, then
it produces null.
Recall that `++case-to-aeon:ze` produces the revision number that a case
corresponds to, if it corresponds to any. If it doesn't yet correspond to a
revision, then it produces null.
Thus, we first check to see if we've even gotten to the beginning of the range
of revisions requested. If not, then we assert that we haven't yet gotten to
@ -1462,8 +1458,9 @@ If there will be more revisions in the subscription, then we call `++duce`,
adding the duct to our subscribers. We modify the rove to start at the next
revision since we've already handled all the revisions up to the present.
We glossed over the calls to `++apax`, `++gack`, and `++bleb`, so we'll get
back to those right now. `++bleb` is simple, so we'll start with that.
We glossed over the calls to `++lobes-at-path`, `++make-nako`, and `++bleb`, so
we'll get back to those right now. `++bleb` is simple, so we'll start with
that.
```
++ bleb :: ship sequence
@ -1477,11 +1474,11 @@ the updates since that revision. We use `++blab` to produce this result to
the subscriber. The case is `%w` with a revision number of the beginning of the
subscription, and the data is the nako itself.
We call `++apax:ze` to get the data at the particular path.
We call `++lobes-at-path:ze` to get the data at the particular path.
```
++ apax :: apax:ze
|= [oan=@ud pax=path] :: data at path
++ lobes-at-path :: lobes-at-path:ze
|= [oan=aeon pax=path] :: data at path
^- (map path lobe)
?: =(0 oan) ~
%- mo
@ -1489,8 +1486,8 @@ We call `++apax:ze` to get the data at the particular path.
%. ~
%~ tap by
=< q
%- ~(got by hut)
%- ~(got by hit)
%- tako-to-yaki
%- aeon-to-tako
oan
|= [p=path q=lobe]
?| ?=(~ pax)
@ -1510,20 +1507,20 @@ the hash of their data. This is simple and efficient to calculate and compare
to later revisions. This allows us to easily tell if a node or its children
have changed.
Finally, we will describe `++gack:ze`.
Finally, we will describe `++make-nako:ze`.
```
++ gack :: gack a through b
|= [a=@ud b=@ud]
^- [(map ,@ud tako) @ud (set yaki) (set blob)]
++ make-nako :: gack a through b
|= [a=aeon b=aeon]
^- [(map aeon tako) aeon (set yaki) (set blob)]
:_ :- b
%- hack
%+ pack
(~(get by hit) a) :: if a not found, a=0
%- need (~(get by hit) b)
^- (map ,@ud tako)
=- [(takos-to-yakis -<) (lobes-to-blobs ->)]
%+ reachable-between-takos
(~(get by hit) a) :: if a not found, a=0
(aeon-to-tako b)
^- (map aeon tako)
%- mo %+ skim (~(tap by hit) ~)
|= [p=@ud *]
|= [p=aeon *]
&((gth p a) (lte p b))
```
@ -1537,18 +1534,18 @@ commits and produce all those numbered greater than `a` and not greater than
The second is even easier to produce -- `b` is clearly our most recent commit.
The third and fourth are slightly more interesting, though not too terribly
difficult. First, we call `++pack`.
difficult. First, we call `++reachable-between-takos`.
```
++ pack
|= [a=(unit tako) b=tako] :: pack a through b
++ reachable-between-takos
|= [a=(unit tako) b=tako] :: pack a through b
^- [(set tako) (set lobe)]
=+ ^= sar
?~ a ~
(zule r:(need (~(get by hut) u.a)))
=+ yak=`yaki`(need (~(get by hut) b))
%+ garf (garg ~ sar) :: get lobes
|- ^- (set tako) :: walk onto sar
(reachable-takos r:(tako-to-yaki u.a))
=+ yak=`yaki`(tako-to-yaki b)
%+ new-lobes-takos (new-lobes ~ sar) :: get lobes
|- ^- (set tako) :: walk onto sar
?: (~(has in sar) r.yak)
~
=+ ber=`(set tako)`(~(put in `(set tako)`~) `tako`r.yak)
@ -1557,71 +1554,33 @@ difficult. First, we call `++pack`.
%+ roll p.yak
|= [yek=tako bar=(set tako)]
^- (set tako)
?: (~(has in bar) yek) :: save some time
?: (~(has in bar) yek) :: save some time
bar
%- ~(uni in bar)
^$(yak (need (~(get by hut) yek)))
^$(yak (tako-to-yaki yek))
```
We take a possible starting commit and a definite ending commit, and we produce
the set of commits and the set of data between them.
We let `sar` be the set of commits reachable from `a`. If `a` is null, then
obviously no commits are reachable. Otherwise, we call `++zule` to calculate this.
```
++ hack :: trivial
|= [a=(set tako) b=(set lobe)]
^- [(set yaki) (set blob)]
:- %- sa %+ turn (~(tap by a) ~)
|= tak=tako
(need (~(get by hut) tak))
%- sa %+ turn (~(tap by b) ~)
|= lob=lobe
(need (~(get by lat) lob))
```
obviously no commits are reachable. Otherwise, we call `++reachable-takos` to
calculate this.
The type signature says everything you need to know about this. We take a set
of hashes of commits and data and convert them into the actual commits and data
in the most straightforward way possible.
```
++ zule :: reachable
|= p=tako :: XX slow
++ reachable-takos :: reachable
|= p=tako :: XX slow
^- (set tako)
=+ y=(~(got by hut) p)
=+ y=(tako-to-yaki p)
=+ t=(~(put in _(set tako)) p)
%+ roll p.y
|= [q=tako s=_t]
?: (~(has in s) q) :: already done
s :: hence skip
(~(uni in s) ^$(p q)) :: otherwise traverse
?: (~(has in s) q) :: already done
s :: hence skip
(~(uni in s) ^$(p q)) :: otherwise traverse
```
```
++ garg :: object hash set
|= [b=(set lobe) a=(set tako)] :: that aren't in b
^- (set lobe)
%+ roll (~(tap in a) ~)
|= [tak=tako bar=(set lobe)]
^- (set lobe)
=+ yak=(need (~(get by hut) tak))
%+ roll (~(tap by q.yak) ~)
|= [[path lob=lobe] far=_bar]
^- (set lobe)
?~ (~(has in b) lob) :: don't need
far
=+ gar=(need (~(get by lat) lob))
?- -.gar
%direct (~(put in far) lob)
%delta (~(put in $(lob q.gar)) lob)
%indirect (~(put in $(lob s.gar)) lob)
==
```
```
++ garf :: garg & repack
|= [b=(set lobe) a=(set tako)]
^- [(set tako) (set lobe)]
[a (garg b a)]
```