Merge pull request #5331 from edolstra/references
Add a test for RefScanSink and clean up the code
This commit is contained in:
commit
d8a2f7f81d
6 changed files with 124 additions and 53 deletions
1
Makefile
1
Makefile
|
@ -4,6 +4,7 @@ makefiles = \
|
||||||
src/libutil/local.mk \
|
src/libutil/local.mk \
|
||||||
src/libutil/tests/local.mk \
|
src/libutil/tests/local.mk \
|
||||||
src/libstore/local.mk \
|
src/libstore/local.mk \
|
||||||
|
src/libstore/tests/local.mk \
|
||||||
src/libfetchers/local.mk \
|
src/libfetchers/local.mk \
|
||||||
src/libmain/local.mk \
|
src/libmain/local.mk \
|
||||||
src/libexpr/local.mk \
|
src/libexpr/local.mk \
|
||||||
|
|
|
@ -2140,8 +2140,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
|
|
||||||
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
||||||
NullSink blank;
|
NullSink blank;
|
||||||
auto references = worker.store.parseStorePathSet(
|
auto references = scanForReferences(blank, actualPath, referenceablePaths);
|
||||||
scanForReferences(blank, actualPath, worker.store.printStorePathSet(referenceablePaths)));
|
|
||||||
|
|
||||||
outputReferencesIfUnregistered.insert_or_assign(
|
outputReferencesIfUnregistered.insert_or_assign(
|
||||||
outputName,
|
outputName,
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static unsigned int refLength = 32; /* characters */
|
static size_t refLength = 32; /* characters */
|
||||||
|
|
||||||
|
|
||||||
static void search(const unsigned char * s, size_t len,
|
static void search(
|
||||||
StringSet & hashes, StringSet & seen)
|
std::string_view s,
|
||||||
|
StringSet & hashes,
|
||||||
|
StringSet & seen)
|
||||||
{
|
{
|
||||||
static std::once_flag initialised;
|
static std::once_flag initialised;
|
||||||
static bool isBase32[256];
|
static bool isBase32[256];
|
||||||
|
@ -25,7 +27,7 @@ static void search(const unsigned char * s, size_t len,
|
||||||
isBase32[(unsigned char) base32Chars[i]] = true;
|
isBase32[(unsigned char) base32Chars[i]] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i + refLength <= len; ) {
|
for (size_t i = 0; i + refLength <= s.size(); ) {
|
||||||
int j;
|
int j;
|
||||||
bool match = true;
|
bool match = true;
|
||||||
for (j = refLength - 1; j >= 0; --j)
|
for (j = refLength - 1; j >= 0; --j)
|
||||||
|
@ -35,7 +37,7 @@ static void search(const unsigned char * s, size_t len,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
string ref((const char *) s + i, refLength);
|
std::string ref(s.substr(i, refLength));
|
||||||
if (hashes.erase(ref)) {
|
if (hashes.erase(ref)) {
|
||||||
debug(format("found reference to '%1%' at offset '%2%'")
|
debug(format("found reference to '%1%' at offset '%2%'")
|
||||||
% ref % i);
|
% ref % i);
|
||||||
|
@ -46,69 +48,60 @@ static void search(const unsigned char * s, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RefScanSink : Sink
|
void RefScanSink::operator () (std::string_view data)
|
||||||
{
|
{
|
||||||
StringSet hashes;
|
/* It's possible that a reference spans the previous and current
|
||||||
StringSet seen;
|
fragment, so search in the concatenation of the tail of the
|
||||||
|
previous fragment and the start of the current fragment. */
|
||||||
|
auto s = tail;
|
||||||
|
s.append(data.data(), refLength);
|
||||||
|
search(s, hashes, seen);
|
||||||
|
|
||||||
string tail;
|
search(data, hashes, seen);
|
||||||
|
|
||||||
RefScanSink() { }
|
auto tailLen = std::min(data.size(), refLength);
|
||||||
|
auto rest = refLength - tailLen;
|
||||||
void operator () (std::string_view data) override
|
if (rest < tail.size())
|
||||||
{
|
tail = tail.substr(tail.size() - rest);
|
||||||
/* It's possible that a reference spans the previous and current
|
tail.append(data.data() + data.size() - tailLen, tailLen);
|
||||||
fragment, so search in the concatenation of the tail of the
|
}
|
||||||
previous fragment and the start of the current fragment. */
|
|
||||||
string s = tail + std::string(data, 0, refLength);
|
|
||||||
search((const unsigned char *) s.data(), s.size(), hashes, seen);
|
|
||||||
|
|
||||||
search((const unsigned char *) data.data(), data.size(), hashes, seen);
|
|
||||||
|
|
||||||
size_t tailLen = data.size() <= refLength ? data.size() : refLength;
|
|
||||||
tail = std::string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen));
|
|
||||||
tail.append({data.data() + data.size() - tailLen, tailLen});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
std::pair<PathSet, HashResult> scanForReferences(const string & path,
|
std::pair<StorePathSet, HashResult> scanForReferences(
|
||||||
const PathSet & refs)
|
const string & path,
|
||||||
|
const StorePathSet & refs)
|
||||||
{
|
{
|
||||||
HashSink hashSink { htSHA256 };
|
HashSink hashSink { htSHA256 };
|
||||||
auto found = scanForReferences(hashSink, path, refs);
|
auto found = scanForReferences(hashSink, path, refs);
|
||||||
auto hash = hashSink.finish();
|
auto hash = hashSink.finish();
|
||||||
return std::pair<PathSet, HashResult>(found, hash);
|
return std::pair<StorePathSet, HashResult>(found, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSet scanForReferences(Sink & toTee,
|
StorePathSet scanForReferences(
|
||||||
const string & path, const PathSet & refs)
|
Sink & toTee,
|
||||||
|
const Path & path,
|
||||||
|
const StorePathSet & refs)
|
||||||
{
|
{
|
||||||
RefScanSink refsSink;
|
StringSet hashes;
|
||||||
TeeSink sink { refsSink, toTee };
|
std::map<std::string, StorePath> backMap;
|
||||||
std::map<string, Path> backMap;
|
|
||||||
|
|
||||||
for (auto & i : refs) {
|
for (auto & i : refs) {
|
||||||
auto baseName = std::string(baseNameOf(i));
|
std::string hashPart(i.hashPart());
|
||||||
string::size_type pos = baseName.find('-');
|
auto inserted = backMap.emplace(hashPart, i).second;
|
||||||
if (pos == string::npos)
|
assert(inserted);
|
||||||
throw Error("bad reference '%1%'", i);
|
hashes.insert(hashPart);
|
||||||
string s = string(baseName, 0, pos);
|
|
||||||
assert(s.size() == refLength);
|
|
||||||
assert(backMap.find(s) == backMap.end());
|
|
||||||
// parseHash(htSHA256, s);
|
|
||||||
refsSink.hashes.insert(s);
|
|
||||||
backMap[s] = i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look for the hashes in the NAR dump of the path. */
|
/* Look for the hashes in the NAR dump of the path. */
|
||||||
|
RefScanSink refsSink(std::move(hashes));
|
||||||
|
TeeSink sink { refsSink, toTee };
|
||||||
dumpPath(path, sink);
|
dumpPath(path, sink);
|
||||||
|
|
||||||
/* Map the hashes found back to their store paths. */
|
/* Map the hashes found back to their store paths. */
|
||||||
PathSet found;
|
StorePathSet found;
|
||||||
for (auto & i : refsSink.seen) {
|
for (auto & i : refsSink.getResult()) {
|
||||||
std::map<string, Path>::iterator j;
|
auto j = backMap.find(i);
|
||||||
if ((j = backMap.find(i)) == backMap.end()) abort();
|
assert(j != backMap.end());
|
||||||
found.insert(j->second);
|
found.insert(j->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "types.hh"
|
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
|
#include "path.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::pair<PathSet, HashResult> scanForReferences(const Path & path, const PathSet & refs);
|
std::pair<StorePathSet, HashResult> scanForReferences(const Path & path, const StorePathSet & refs);
|
||||||
|
|
||||||
PathSet scanForReferences(Sink & toTee, const Path & path, const PathSet & refs);
|
StorePathSet scanForReferences(Sink & toTee, const Path & path, const StorePathSet & refs);
|
||||||
|
|
||||||
|
class RefScanSink : public Sink
|
||||||
|
{
|
||||||
|
StringSet hashes;
|
||||||
|
StringSet seen;
|
||||||
|
|
||||||
|
std::string tail;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RefScanSink(StringSet && hashes) : hashes(hashes)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
StringSet & getResult()
|
||||||
|
{ return seen; }
|
||||||
|
|
||||||
|
void operator () (std::string_view data) override;
|
||||||
|
};
|
||||||
|
|
||||||
struct RewritingSink : Sink
|
struct RewritingSink : Sink
|
||||||
{
|
{
|
||||||
|
|
15
src/libstore/tests/local.mk
Normal file
15
src/libstore/tests/local.mk
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
check: libstore-tests_RUN
|
||||||
|
|
||||||
|
programs += libstore-tests
|
||||||
|
|
||||||
|
libstore-tests_DIR := $(d)
|
||||||
|
|
||||||
|
libstore-tests_INSTALL_DIR :=
|
||||||
|
|
||||||
|
libstore-tests_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
|
libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil
|
||||||
|
|
||||||
|
libstore-tests_LIBS = libstore
|
||||||
|
|
||||||
|
libstore-tests_LDFLAGS := $(GTEST_LIBS)
|
45
src/libstore/tests/references.cc
Normal file
45
src/libstore/tests/references.cc
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "references.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(references, scan)
|
||||||
|
{
|
||||||
|
std::string hash1 = "dc04vv14dak1c1r48qa0m23vr9jy8sm0";
|
||||||
|
std::string hash2 = "zc842j0rz61mjsp3h3wp5ly71ak6qgdn";
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1});
|
||||||
|
auto s = "foobar";
|
||||||
|
scanner(s);
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy";
|
||||||
|
scanner(s);
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet{hash1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1, hash2});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy" + hash2;
|
||||||
|
scanner(((std::string_view) s).substr(0, 10));
|
||||||
|
scanner(((std::string_view) s).substr(10, 5));
|
||||||
|
scanner(((std::string_view) s).substr(15, 5));
|
||||||
|
scanner(((std::string_view) s).substr(20));
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1, hash2});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy" + hash2;
|
||||||
|
for (auto & i : s)
|
||||||
|
scanner(std::string(1, i));
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue