Files
2026-03-13 09:53:40 +01:00

259 lines
7.2 KiB
C++

#ifndef BUILDING_NODE_EXTENSION
#define BUILDING_NODE_EXTENSION
#endif
#ifndef LIBLZMA_NODE_HPP
#define LIBLZMA_NODE_HPP
#include <napi.h>
#include <lzma.h>
#include "index-parser.h"
#include <vector>
#include <list>
#include <set>
#include <queue>
#include <string>
#include <utility>
#include <atomic>
#include <mutex>
#include <memory>
namespace lzma {
using namespace Napi;
/* util */
/**
* Return the filter constant associated with a v8 String handle
*/
lzma_vli FilterByName(Value name);
/**
* If rv represents an error, throw a javascript exception representing it.
* Always returns rv as a v8 Integer.
*/
Number lzmaRet(Env env, lzma_ret rv);
/**
* Return a javascript exception representing rv.
*/
Error lzmaRetError(Env env, lzma_ret rv);
/**
* Takes a Node.js SlowBuffer or Buffer as input and populates data accordingly.
* Returns true on success, false on failure.
*/
bool readBufferFromObj(Value value, std::vector<uint8_t>* data);
/**
* Return a lzma_options_lzma struct as described by the v8 Object obj.
*/
lzma_options_lzma parseOptionsLZMA(Value obj);
/**
* Return a v8 Number representation of an uint64_t where UINT64_MAX will be mapped to null
*/
Value Uint64ToNumberMaxNull(Env env, uint64_t in);
/**
* Return a v8 Number representation of an uint64_t where 0 will be mapped to null
*/
Value Uint64ToNumber0Null(Env env, uint64_t in);
/**
* Return a uint64_t representation of a v8 Number,
* where values above UINT64_MAX map to UINT64_MAX and null to UINT64_MAX.
* Throws an TypeError if the input is not a number.
*/
uint64_t NumberToUint64ClampNullMax(Value in);
/**
* Return an integer property of an object (which can be passed to Nan::Get),
* providing a default value if no such property is present
*/
inline int64_t GetIntegerProperty(Object obj, const char* name, int64_t def) {
Value v = obj[name];
if (v.IsUndefined())
return def;
return v.ToNumber().Int64Value();
}
/* bindings in one-to-one correspondence to the lzma functions */
Value lzmaVersionNumber(const CallbackInfo& info);
Value lzmaVersionString(const CallbackInfo& info);
Value lzmaCheckIsSupported(const CallbackInfo& info);
Value lzmaCheckSize(const CallbackInfo& info);
Value lzmaFilterEncoderIsSupported(const CallbackInfo& info);
Value lzmaFilterDecoderIsSupported(const CallbackInfo& info);
Value lzmaMfIsSupported(const CallbackInfo& info);
Value lzmaModeIsSupported(const CallbackInfo& info);
Value lzmaEasyEncoderMemusage(const CallbackInfo& info);
Value lzmaEasyDecoderMemusage(const CallbackInfo& info);
Value lzmaCRC32(const CallbackInfo& info);
Value lzmaRawEncoderMemusage(const CallbackInfo& info);
Value lzmaRawDecoderMemusage(const CallbackInfo& info);
/* wrappers */
/**
* List of liblzma filters with corresponding options
*/
class FilterArray {
public:
FilterArray() = default;
explicit FilterArray(Value arr);
lzma_filter* array() { return filters.data(); }
const lzma_filter* array() const { return filters.data(); }
private:
FilterArray(const FilterArray&);
FilterArray& operator=(const FilterArray&);
union options {
lzma_options_delta delta;
lzma_options_lzma lzma;
};
std::vector<lzma_filter> filters;
std::list<options> optbuf;
};
/**
* Wrapper for lzma_mt (multi-threading options).
*/
class MTOptions {
public:
MTOptions() = default;
explicit MTOptions(Value val);
lzma_mt* opts() { return &opts_; }
const lzma_mt* opts() const { return &opts_; }
private:
std::unique_ptr<FilterArray> filters_;
lzma_mt opts_;
};
/**
* Node.js object wrap for lzma_stream wrapper. Corresponds to exports.Stream
*/
class LZMAStream : public ObjectWrap<LZMAStream> {
public:
explicit LZMAStream(const CallbackInfo& info);
~LZMAStream();
static void InitializeExports(Object exports);
/* regard as private: */
void doLZMACodeFromAsync();
void invokeBufferHandlers(bool hasLock);
void* alloc(size_t nmemb, size_t size);
void free(void* ptr);
private:
void resetUnderlying();
void doLZMACode();
static Napi::Value New(const CallbackInfo& info);
void adjustExternalMemory(int64_t bytesChange);
void reportAdjustedExternalMemoryToV8();
struct MemScope {
explicit MemScope(LZMAStream* stream) : stream(stream) { }
~MemScope() { stream->reportAdjustedExternalMemoryToV8(); }
LZMAStream* stream;
};
AsyncContext async_context;
std::atomic<int64_t> nonAdjustedExternalMemory;
std::mutex mutex;
void ResetUnderlying(const CallbackInfo& info);
Napi::Value SetBufsize(const CallbackInfo& info);
void Code(const CallbackInfo& info);
Napi::Value Memusage(const CallbackInfo& info);
Napi::Value MemlimitGet(const CallbackInfo& info);
Napi::Value MemlimitSet(const CallbackInfo& info);
Napi::Value RawEncoder(const CallbackInfo& info);
Napi::Value RawDecoder(const CallbackInfo& info);
Napi::Value FiltersUpdate(const CallbackInfo& info);
Napi::Value EasyEncoder(const CallbackInfo& info);
Napi::Value StreamEncoder(const CallbackInfo& info);
Napi::Value AloneEncoder(const CallbackInfo& info);
Napi::Value MTEncoder(const CallbackInfo& info);
Napi::Value StreamDecoder(const CallbackInfo& info);
Napi::Value AutoDecoder(const CallbackInfo& info);
Napi::Value AloneDecoder(const CallbackInfo& info);
lzma_allocator allocator;
lzma_stream _;
size_t bufsize;
std::string error;
bool shouldFinish;
size_t processedChunks;
lzma_ret lastCodeResult;
std::queue<std::vector<uint8_t>> inbufs;
std::queue<std::vector<uint8_t>> outbufs;
};
/**
* Async worker for a single coding step.
*/
class LZMAStreamCodingWorker : public AsyncWorker {
public:
LZMAStreamCodingWorker(LZMAStream* stream_)
: AsyncWorker(Function(stream_->Env(), nullptr), "LZMAStreamCodingWorker"),
stream(stream_) {
Receiver().Set(static_cast<uint32_t>(0), stream->Value());
}
~LZMAStreamCodingWorker() {}
void Execute() override {
stream->doLZMACodeFromAsync();
}
private:
void OnOK() {
stream->invokeBufferHandlers(false);
}
void OnOK(const Error& e) {
stream->invokeBufferHandlers(false);
}
LZMAStream* stream;
};
class IndexParser : public ObjectWrap<IndexParser> {
public:
explicit IndexParser(const CallbackInfo& info);
~IndexParser();
static void InitializeExports(Object exports);
/* regard as private: */
int64_t readCallback(void* opaque, uint8_t* buf, size_t count, int64_t offset);
private:
lzma_index_parser_data info;
lzma_allocator allocator;
uint8_t* currentReadBuffer;
size_t currentReadSize;
bool isCurrentlyInParseCall;
Object getObject() const;
void Init(const CallbackInfo& info);
Napi::Value Feed(const CallbackInfo& info);
Napi::Value Parse(const CallbackInfo& info);
};
}
#endif