projects/guix·blob python-team · refs

113 lines · 3.0 KiB

1#include "references.hh"
2#include "hash.hh"
3#include "util.hh"
4#include "archive.hh"
5
6#include <map>
7#include <cstdlib>
8#include <cassert>
9#include <format>
10
11namespace nix {
12
13
14static unsigned int refLength = 32; /* characters */
15
16
17static void search(const unsigned char * s, unsigned int len,
18 StringSet & hashes, StringSet & seen)
19{
20 for (unsigned int i = 0; i + refLength <= len; ) {
21 int j;
22 bool match = true;
23 for (j = refLength - 1; j >= 0; --j)
24 if (base32Values[(unsigned char) s[i + j]] == -1) {
25 i += j + 1;
26 match = false;
27 break;
28 }
29 if (!match) continue;
30 string ref((const char *) s + i, refLength);
31 if (hashes.find(ref) != hashes.end()) {
32 debug(std::format("found reference to `{}' at offset `{}'", ref, i));
33 seen.insert(ref);
34 hashes.erase(ref);
35 }
36 ++i;
37 }
38}
39
40
41struct RefScanSink : Sink
42{
43 HashSink hashSink;
44 StringSet hashes;
45 StringSet seen;
46
47 string tail;
48
49 RefScanSink() : hashSink(htSHA256) { }
50
51 void operator () (const unsigned char * data, size_t len);
52};
53
54
55void RefScanSink::operator () (const unsigned char * data, size_t len)
56{
57 hashSink(data, len);
58
59 /* It's possible that a reference spans the previous and current
60 fragment, so search in the concatenation of the tail of the
61 previous fragment and the start of the current fragment. */
62 string s = tail + string((const char *) data, len > refLength ? refLength : len);
63 search((const unsigned char *) s.data(), s.size(), hashes, seen);
64
65 search(data, len, hashes, seen);
66
67 unsigned int tailLen = len <= refLength ? len : refLength;
68 tail =
69 string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen)) +
70 string((const char *) data + len - tailLen, tailLen);
71}
72
73
74PathSet scanForReferences(const string & path,
75 const PathSet & refs, HashResult & hash)
76{
77 RefScanSink sink;
78 std::map<string, Path> backMap;
79
80 /* For efficiency (and a higher hit rate), just search for the
81 hash part of the file name. (This assumes that all references
82 have the form `HASH-bla'). */
83 for (const auto& i : refs) {
84 string baseName = baseNameOf(i);
85 string::size_type pos = baseName.find('-');
86 if (pos == string::npos)
87 throw Error(std::format("bad reference `{}'", i));
88 string s = string(baseName, 0, pos);
89 assert(s.size() == refLength);
90 assert(backMap.find(s) == backMap.end());
91 // parseHash(htSHA256, s);
92 sink.hashes.insert(s);
93 backMap[s] = i;
94 }
95
96 /* Look for the hashes in the NAR dump of the path. */
97 dumpPath(path, sink);
98
99 /* Map the hashes found back to their store paths. */
100 PathSet found;
101 for (auto& i : sink.seen) {
102 std::map<string, Path>::iterator j;
103 if ((j = backMap.find(i)) == backMap.end()) abort();
104 found.insert(j->second);
105 }
106
107 hash = sink.hashSink.finish();
108
109 return found;
110}
111
112
113}