1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-27 04:21:16 +02:00

Merge branch 'master' into new-cli

This commit is contained in:
Eelco Dolstra 2016-02-25 11:25:11 +01:00
commit f1bdeac986
74 changed files with 2119 additions and 540 deletions

View file

@ -29,7 +29,7 @@ bool useCaseHack =
false;
#endif
static string archiveVersion1 = "nix-archive-1";
const std::string narVersionMagic1 = "nix-archive-1";
static string caseHackSuffix = "~nix~case~hack~";
@ -113,11 +113,17 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
{
sink << archiveVersion1;
sink << narVersionMagic1;
dump(path, sink, filter);
}
void dumpString(const std::string & s, Sink & sink)
{
sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")";
}
static SerialisationError badArchive(string s)
{
return SerialisationError("bad archive: " + s);
@ -214,7 +220,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
}
else if (s == "executable" && type == tpRegular) {
readString(source);
auto s = readString(source);
if (s != "") throw badArchive("executable marker has non-empty value");
sink.isExecutable();
}
@ -275,7 +282,7 @@ void parseDump(ParseSink & sink, Source & source)
/* This generally means the integer at the start couldn't be
decoded. Ignore and throw the exception below. */
}
if (version != archiveVersion1)
if (version != narVersionMagic1)
throw badArchive("input doesn't look like a Nix archive");
parse(sink, source, "");
}

View file

@ -55,6 +55,8 @@ extern PathFilter defaultPathFilter;
void dumpPath(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
void dumpString(const std::string & s, Sink & sink);
struct ParseSink
{
virtual void createDirectory(const Path & path) { };
@ -76,4 +78,7 @@ void restorePath(const Path & path, Source & source);
extern bool useCaseHack;
extern const std::string narVersionMagic1;
}

View file

@ -6,34 +6,83 @@
namespace nix {
/* RAII wrapper around lzma_stream. */
struct LzmaStream
{
lzma_stream strm;
LzmaStream() : strm(LZMA_STREAM_INIT) { };
~LzmaStream() { lzma_end(&strm); };
lzma_stream & operator()() { return strm; }
};
std::string compressXZ(const std::string & in)
{
LzmaStream strm;
// FIXME: apply the x86 BCJ filter?
lzma_ret ret = lzma_easy_encoder(
&strm(), 6, LZMA_CHECK_CRC64);
if (ret != LZMA_OK)
throw Error("unable to initialise lzma encoder");
lzma_action action = LZMA_RUN;
uint8_t outbuf[BUFSIZ];
string res;
strm().next_in = (uint8_t *) in.c_str();
strm().avail_in = in.size();
strm().next_out = outbuf;
strm().avail_out = sizeof(outbuf);
while (true) {
if (strm().avail_in == 0)
action = LZMA_FINISH;
lzma_ret ret = lzma_code(&strm(), action);
if (strm().avail_out == 0 || ret == LZMA_STREAM_END) {
res.append((char *) outbuf, sizeof(outbuf) - strm().avail_out);
strm().next_out = outbuf;
strm().avail_out = sizeof(outbuf);
}
if (ret == LZMA_STREAM_END)
return res;
if (ret != LZMA_OK)
throw Error("error while decompressing xz file");
}
}
std::string decompressXZ(const std::string & in)
{
lzma_stream strm = LZMA_STREAM_INIT;
LzmaStream strm;
lzma_ret ret = lzma_stream_decoder(
&strm, UINT64_MAX, LZMA_CONCATENATED);
&strm(), UINT64_MAX, LZMA_CONCATENATED);
if (ret != LZMA_OK)
throw Error("unable to initialise lzma decoder");
lzma_action action = LZMA_RUN;
uint8_t outbuf[BUFSIZ];
string res;
strm.next_in = (uint8_t *) in.c_str();
strm.avail_in = in.size();
strm.next_out = outbuf;
strm.avail_out = sizeof(outbuf);
strm().next_in = (uint8_t *) in.c_str();
strm().avail_in = in.size();
strm().next_out = outbuf;
strm().avail_out = sizeof(outbuf);
while (true) {
if (strm.avail_in == 0)
if (strm().avail_in == 0)
action = LZMA_FINISH;
lzma_ret ret = lzma_code(&strm, action);
lzma_ret ret = lzma_code(&strm(), action);
if (strm.avail_out == 0 || ret == LZMA_STREAM_END) {
res.append((char *) outbuf, sizeof(outbuf) - strm.avail_out);
strm.next_out = outbuf;
strm.avail_out = sizeof(outbuf);
if (strm().avail_out == 0 || ret == LZMA_STREAM_END) {
res.append((char *) outbuf, sizeof(outbuf) - strm().avail_out);
strm().next_out = outbuf;
strm().avail_out = sizeof(outbuf);
}
if (ret == LZMA_STREAM_END)

View file

@ -4,6 +4,8 @@
namespace nix {
std::string compressXZ(const std::string & in);
std::string decompressXZ(const std::string & in);
}

84
src/libutil/lru-cache.hh Normal file
View file

@ -0,0 +1,84 @@
#pragma once
#include <map>
#include <list>
namespace nix {
/* A simple least-recently used cache. Not thread-safe. */
template<typename Key, typename Value>
class LRUCache
{
private:
size_t maxSize;
// Stupid wrapper to get around circular dependency between Data
// and LRU.
struct LRUIterator;
using Data = std::map<Key, std::pair<LRUIterator, Value>>;
using LRU = std::list<typename Data::iterator>;
struct LRUIterator { typename LRU::iterator it; };
Data data;
LRU lru;
public:
LRUCache(size_t maxSize) : maxSize(maxSize) { }
/* Insert or upsert an item in the cache. */
void upsert(const Key & key, const Value & value)
{
erase(key);
if (data.size() >= maxSize) {
/* Retire the oldest item. */
auto oldest = lru.begin();
data.erase(*oldest);
lru.erase(oldest);
}
auto res = data.emplace(key, std::make_pair(LRUIterator(), value));
assert(res.second);
auto & i(res.first);
auto j = lru.insert(lru.end(), i);
i->second.first.it = j;
}
bool erase(const Key & key)
{
auto i = data.find(key);
if (i == data.end()) return false;
lru.erase(i->second.first.it);
data.erase(i);
return true;
}
/* Look up an item in the cache. If it exists, it becomes the most
recently used item. */
// FIXME: use boost::optional?
Value * get(const Key & key)
{
auto i = data.find(key);
if (i == data.end()) return 0;
/* Move this item to the back of the LRU list. */
lru.erase(i->second.first.it);
auto j = lru.insert(lru.end(), i);
i->second.first.it = j;
return &i->second.second;
}
size_t size()
{
return data.size();
}
};
}

151
src/libutil/pool.hh Normal file
View file

@ -0,0 +1,151 @@
#pragma once
#include <functional>
#include <limits>
#include <list>
#include <memory>
#include <cassert>
#include "sync.hh"
#include "ref.hh"
namespace nix {
/* This template class implements a simple pool manager of resources
of some type R, such as database connections. It is used as
follows:
class Connection { ... };
Pool<Connection> pool;
{
auto conn(pool.get());
conn->exec("select ...");
}
Here, the Connection object referenced by conn is automatically
returned to the pool when conn goes out of scope.
*/
template <class R>
class Pool
{
public:
/* A function that produces new instances of R on demand. */
typedef std::function<ref<R>()> Factory;
/* A function that checks whether an instance of R is still
usable. Unusable instances are removed from the pool. */
typedef std::function<bool(const ref<R> &)> Validator;
private:
Factory factory;
Validator validator;
struct State
{
size_t inUse = 0;
size_t max;
std::vector<ref<R>> idle;
};
Sync<State> state;
std::condition_variable wakeup;
public:
Pool(size_t max = std::numeric_limits<size_t>::max(),
const Factory & factory = []() { return make_ref<R>(); },
const Validator & validator = [](ref<R> r) { return true; })
: factory(factory)
, validator(validator)
{
auto state_(state.lock());
state_->max = max;
}
~Pool()
{
auto state_(state.lock());
assert(!state_->inUse);
state_->max = 0;
state_->idle.clear();
}
class Handle
{
private:
Pool & pool;
std::shared_ptr<R> r;
friend Pool;
Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { }
public:
Handle(Handle && h) : pool(h.pool), r(h.r) { h.r.reset(); }
Handle(const Handle & l) = delete;
~Handle()
{
if (!r) return;
{
auto state_(pool.state.lock());
state_->idle.push_back(ref<R>(r));
assert(state_->inUse);
state_->inUse--;
}
pool.wakeup.notify_one();
}
R * operator -> () { return &*r; }
R & operator * () { return *r; }
};
Handle get()
{
{
auto state_(state.lock());
/* If we're over the maximum number of instance, we need
to wait until a slot becomes available. */
while (state_->idle.empty() && state_->inUse >= state_->max)
state_.wait(wakeup);
while (!state_->idle.empty()) {
auto p = state_->idle.back();
state_->idle.pop_back();
if (validator(p)) {
state_->inUse++;
return Handle(*this, p);
}
}
state_->inUse++;
}
/* We need to create a new instance. Because that might take a
while, we don't hold the lock in the meantime. */
try {
Handle h(*this, factory());
return h;
} catch (...) {
auto state_(state.lock());
state_->inUse--;
throw;
}
}
unsigned int count()
{
auto state_(state.lock());
return state_->idle.size() + state_->inUse;
}
};
}

67
src/libutil/ref.hh Normal file
View file

@ -0,0 +1,67 @@
#pragma once
#include <memory>
#include <exception>
namespace nix {
/* A simple non-nullable reference-counted pointer. Actually a wrapper
around std::shared_ptr that prevents non-null constructions. */
template<typename T>
class ref
{
private:
std::shared_ptr<T> p;
public:
ref<T>(const ref<T> & r)
: p(r.p)
{ }
explicit ref<T>(const std::shared_ptr<T> & p)
: p(p)
{
if (!p)
throw std::invalid_argument("null pointer cast to ref");
}
T* operator ->() const
{
return &*p;
}
T& operator *() const
{
return *p;
}
operator std::shared_ptr<T> ()
{
return p;
}
template<typename T2>
operator ref<T2> ()
{
return ref<T2>((std::shared_ptr<T2>) p);
}
private:
template<typename T2, typename... Args>
friend ref<T2>
make_ref(Args&&... args);
};
template<typename T, typename... Args>
inline ref<T>
make_ref(Args&&... args)
{
auto p = std::make_shared<T>(std::forward<Args>(args)...);
return ref<T>(p);
}
}

View file

@ -72,7 +72,17 @@ void FdSink::write(const unsigned char * data, size_t len)
warned = true;
}
}
writeFull(fd, data, len);
try {
writeFull(fd, data, len);
} catch (SysError & e) {
_good = true;
}
}
bool FdSink::good()
{
return _good;
}
@ -119,12 +129,18 @@ size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
checkInterrupt();
n = ::read(fd, (char *) data, bufSize);
} while (n == -1 && errno == EINTR);
if (n == -1) throw SysError("reading from file");
if (n == 0) throw EndOfFile("unexpected end-of-file");
if (n == -1) { _good = false; throw SysError("reading from file"); }
if (n == 0) { _good = false; throw EndOfFile("unexpected end-of-file"); }
return n;
}
bool FdSource::good()
{
return _good;
}
size_t StringSource::read(unsigned char * data, size_t len)
{
if (pos == s.size()) throw EndOfFile("end of string reached");

View file

@ -12,6 +12,7 @@ struct Sink
{
virtual ~Sink() { }
virtual void operator () (const unsigned char * data, size_t len) = 0;
virtual bool good() { return true; }
};
@ -25,7 +26,7 @@ struct BufferedSink : Sink
: bufSize(bufSize), bufPos(0), buffer(0) { }
~BufferedSink();
void operator () (const unsigned char * data, size_t len);
void operator () (const unsigned char * data, size_t len) override;
void flush();
@ -47,6 +48,8 @@ struct Source
return the number of bytes stored. If blocks until at least
one byte is available. */
virtual size_t read(unsigned char * data, size_t len) = 0;
virtual bool good() { return true; }
};
@ -60,7 +63,7 @@ struct BufferedSource : Source
: bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(0) { }
~BufferedSource();
size_t read(unsigned char * data, size_t len);
size_t read(unsigned char * data, size_t len) override;
/* Underlying read call, to be overridden. */
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
@ -80,7 +83,12 @@ struct FdSink : BufferedSink
FdSink(int fd) : fd(fd), warn(false), written(0) { }
~FdSink();
void write(const unsigned char * data, size_t len);
void write(const unsigned char * data, size_t len) override;
bool good() override;
private:
bool _good = true;
};
@ -90,7 +98,10 @@ struct FdSource : BufferedSource
int fd;
FdSource() : fd(-1) { }
FdSource(int fd) : fd(fd) { }
size_t readUnbuffered(unsigned char * data, size_t len);
size_t readUnbuffered(unsigned char * data, size_t len) override;
bool good() override;
private:
bool _good = true;
};
@ -98,7 +109,7 @@ struct FdSource : BufferedSource
struct StringSink : Sink
{
string s;
void operator () (const unsigned char * data, size_t len);
void operator () (const unsigned char * data, size_t len) override;
};
@ -108,7 +119,7 @@ struct StringSource : Source
const string & s;
size_t pos;
StringSource(const string & _s) : s(_s), pos(0) { }
size_t read(unsigned char * data, size_t len);
size_t read(unsigned char * data, size_t len) override;
};

78
src/libutil/sync.hh Normal file
View file

@ -0,0 +1,78 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include <cassert>
namespace nix {
/* This template class ensures synchronized access to a value of type
T. It is used as follows:
struct Data { int x; ... };
Sync<Data> data;
{
auto data_(data.lock());
data_->x = 123;
}
Here, "data" is automatically unlocked when "data_" goes out of
scope.
*/
template<class T>
class Sync
{
private:
std::mutex mutex;
T data;
public:
Sync() { }
Sync(const T & data) : data(data) { }
class Lock
{
private:
Sync * s;
std::unique_lock<std::mutex> lk;
friend Sync;
Lock(Sync * s) : s(s), lk(s->mutex) { }
public:
Lock(Lock && l) : s(l.s) { abort(); }
Lock(const Lock & l) = delete;
~Lock() { }
T * operator -> () { return &s->data; }
T & operator * () { return s->data; }
void wait(std::condition_variable & cv)
{
assert(s);
cv.wait(lk);
}
template<class Rep, class Period, class Predicate>
bool wait_for(std::condition_variable & cv,
const std::chrono::duration<Rep, Period> & duration,
Predicate pred)
{
assert(s);
return cv.wait_for(lk, duration, pred);
}
template<class Clock, class Duration>
std::cv_status wait_until(std::condition_variable & cv,
const std::chrono::time_point<Clock, Duration> & duration)
{
assert(s);
return cv.wait_until(lk, duration);
}
};
Lock lock() { return Lock(this); }
};
}

View file

@ -2,6 +2,8 @@
#include "config.h"
#include "ref.hh"
#include <string>
#include <list>
#include <set>
@ -97,70 +99,4 @@ typedef enum {
} Verbosity;
/* A simple non-nullable reference-counted pointer. Actually a wrapper
around std::shared_ptr that prevents non-null constructions. */
template<typename T>
class ref
{
private:
std::shared_ptr<T> p;
public:
ref<T>(const ref<T> & r)
: p(r.p)
{ }
explicit ref<T>(const std::shared_ptr<T> & p)
: p(p)
{
if (!p)
throw std::invalid_argument("null pointer cast to ref");
}
T* operator ->() const
{
return &*p;
}
T& operator *() const
{
return *p;
}
operator std::shared_ptr<T> ()
{
return p;
}
private:
template<typename T2, typename... Args>
friend ref<T2>
make_ref(Args&&... args);
template<typename T2, typename T3, typename... Args>
friend ref<T2>
make_ref(Args&&... args);
};
template<typename T, typename... Args>
inline ref<T>
make_ref(Args&&... args)
{
auto p = std::make_shared<T>(std::forward<Args>(args)...);
return ref<T>(p);
}
template<typename T, typename T2, typename... Args>
inline ref<T>
make_ref(Args&&... args)
{
auto p = std::make_shared<T2>(std::forward<Args>(args)...);
return ref<T>(p);
}
}

View file

@ -320,9 +320,11 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
{
checkInterrupt();
printMsg(lvlVomit, format("%1%") % path);
struct stat st = lstat(path);
struct stat st;
if (lstat(path.c_str(), &st) == -1) {
if (errno == ENOENT) return;
throw SysError(format("getting status of %1%") % path);
}
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
bytesFreed += st.st_blocks * 512;
@ -338,8 +340,10 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
_deletePath(path + "/" + i.name, bytesFreed);
}
if (remove(path.c_str()) == -1)
if (remove(path.c_str()) == -1) {
if (errno == ENOENT) return;
throw SysError(format("cannot unlink %1%") % path);
}
}

View file

@ -92,8 +92,8 @@ string readLine(int fd);
void writeLine(int fd, string s);
/* Delete a path; i.e., in the case of a directory, it is deleted
recursively. Don't use this at home, kids. The second variant
returns the number of bytes and blocks freed. */
recursively. It's not an error if the path does not exist. The
second variant returns the number of bytes and blocks freed. */
void deletePath(const Path & path);
void deletePath(const Path & path, unsigned long long & bytesFreed);
@ -366,6 +366,14 @@ template<class N> bool string2Int(const string & s, N & n)
return str && str.get() == EOF;
}
/* Parse a string into a float. */
template<class N> bool string2Float(const string & s, N & n)
{
std::istringstream str(s);
str >> n;
return str && str.get() == EOF;
}
/* Return true iff `s' ends in `suffix'. */
bool hasSuffix(const string & s, const string & suffix);