Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "printf" support to Encoder #237

Merged
merged 8 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions API/fleece/FLEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ extern "C" {
/** Writes a Fleece Value to an Encoder. */
FLEECE_PUBLIC bool FLEncoder_WriteValue(FLEncoder, FLValue) FLAPI;

/** Writes an Array or Dict to the encoder, as per the format string and printf-style arguments.
For details, see documentation of \ref FLValue_NewWithFormat. */
FLEECE_PUBLIC bool FLEncoder_WriteFormatted(FLEncoder, const char* format, ...) FLAPI __printflike(2, 3);

/** Writes an Array or Dict to the encoder, as per the format string and `va_list`.
For details, see documentation of \ref FLValue_NewWithFormat. */
FLEECE_PUBLIC bool FLEncoder_WriteFormattedArgs(FLEncoder, const char* format, va_list args) FLAPI;

/** Begins writing an array value to an encoder. This pushes a new state where each
subsequent value written becomes an array item, until FLEncoder_EndArray is called.
Expand Down
21 changes: 21 additions & 0 deletions API/fleece/Fleece.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "Fleece.h"
#endif
#include "slice.hh"
#include <cstdarg>
#include <stdexcept>
#include <string>
#include <utility>
Expand Down Expand Up @@ -439,6 +440,9 @@ namespace fleece {
inline bool writeValue(Value);
inline bool convertJSON(slice_NONNULL);

inline bool writeFormatted(const char* format, ...) __printflike(2, 3);
inline bool writeFormattedArgs(const char* format, va_list args);

inline bool beginArray(size_t reserveCount =0);
inline bool endArray();
inline bool beginDict(size_t reserveCount =0);
Expand All @@ -449,6 +453,11 @@ namespace fleece {
template <class T>
inline void write(slice_NONNULL key, T value) {writeKey(key); *this << value;}

template <typename FN>
bool writeArray(FN const& fn) {return beginArray() && (fn(), endArray());}
template <typename FN>
bool writeDict(FN const& fn) {return beginDict() && (fn(), endDict());}

[[nodiscard]] inline Doc finishDoc(FLError* FL_NULLABLE =nullptr);
[[nodiscard]] inline alloc_slice finish(FLError* FL_NULLABLE =nullptr);
inline void reset();
Expand Down Expand Up @@ -604,6 +613,18 @@ namespace fleece {
inline FLError Encoder::error() const {return FLEncoder_GetError(_enc);}
inline const char* Encoder::errorMessage() const {return FLEncoder_GetErrorMessage(_enc);}

inline bool Encoder::writeFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
bool ok = FLEncoder_WriteFormattedArgs(_enc, format, args);
va_end(args);
return ok;
}

inline bool Encoder::writeFormattedArgs(const char* format, va_list args) {
return FLEncoder_WriteFormattedArgs(_enc, format, args);
}

// specialization for assigning bool value since there is no Encoder<<bool
template<>
inline void Encoder::keyref::operator= (bool value) {_enc.writeKey(_key); _enc.writeBool(value);}
Expand Down
9 changes: 9 additions & 0 deletions Fleece.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
279AC53C1C097941002C80DB /* Value+Dump.cc in Sources */ = {isa = PBXBuildFile; fileRef = 279AC53B1C097941002C80DB /* Value+Dump.cc */; };
27A0E3DF24DCD86900380563 /* ConcurrentArena.hh in Headers */ = {isa = PBXBuildFile; fileRef = 27A0E3DD24DCD86900380563 /* ConcurrentArena.hh */; };
27A0E3E024DCD86900380563 /* ConcurrentArena.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27A0E3DE24DCD86900380563 /* ConcurrentArena.cc */; };
27A1327B2C700D45008E84FA /* JSLexer.hh in Headers */ = {isa = PBXBuildFile; fileRef = 27A1327A2C700D45008E84FA /* JSLexer.hh */; };
27A132812C73BF8C008E84FA /* FLEncoder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27A132802C73BF8C008E84FA /* FLEncoder.cc */; };
27A2F73B21248DA50081927B /* FLSlice.h in Headers */ = {isa = PBXBuildFile; fileRef = 27A2F73A21248DA40081927B /* FLSlice.h */; };
27A924CF1D9C32E800086206 /* Path.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27A924CD1D9C32E800086206 /* Path.cc */; };
27A924D01D9C32E800086206 /* Path.hh in Headers */ = {isa = PBXBuildFile; fileRef = 27A924CE1D9C32E800086206 /* Path.hh */; };
Expand Down Expand Up @@ -410,6 +412,8 @@
279AC53B1C097941002C80DB /* Value+Dump.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "Value+Dump.cc"; sourceTree = "<group>"; };
27A0E3DD24DCD86900380563 /* ConcurrentArena.hh */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConcurrentArena.hh; sourceTree = "<group>"; };
27A0E3DE24DCD86900380563 /* ConcurrentArena.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ConcurrentArena.cc; sourceTree = "<group>"; };
27A1327A2C700D45008E84FA /* JSLexer.hh */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JSLexer.hh; sourceTree = "<group>"; };
27A132802C73BF8C008E84FA /* FLEncoder.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FLEncoder.cc; sourceTree = "<group>"; };
27A2F73A21248DA40081927B /* FLSlice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLSlice.h; sourceTree = "<group>"; };
27A63F38263375B500634F7B /* date.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = date.h; sourceTree = "<group>"; };
27A924CD1D9C32E800086206 /* Path.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Path.cc; sourceTree = "<group>"; };
Expand Down Expand Up @@ -587,6 +591,7 @@
isa = PBXGroup;
children = (
278163B31CE69CA800B94E32 /* Fleece.cc */,
27A132802C73BF8C008E84FA /* FLEncoder.cc */,
275B3595234BE12800FE9CF0 /* FLSlice.cc */,
270515551D90596000D62D05 /* Fleece+ImplGlue.hh */,
);
Expand Down Expand Up @@ -856,6 +861,7 @@
27298E7F1C04E665000CFBA8 /* Encoder.cc */,
270FA26F1BF53CEA005DCB13 /* Encoder.hh */,
27E3CE0E263B1B0700CA7056 /* Builder.cc */,
27A1327A2C700D45008E84FA /* JSLexer.hh */,
27E3CE0D263B1B0700CA7056 /* Builder.hh */,
27298E3A1C00F812000CFBA8 /* JSONConverter.cc */,
27298E761C00FB48000CFBA8 /* JSONConverter.hh */,
Expand Down Expand Up @@ -971,6 +977,8 @@
278343132A675A7000621050 /* function_ref.hh in Headers */,
270FA2801BF53CEA005DCB13 /* Writer.hh in Headers */,
270FA27D1BF53CEA005DCB13 /* Encoder.hh in Headers */,
27C8DF072084102900A99BFC /* MutableHashTree.hh in Headers */,
27A1327B2C700D45008E84FA /* JSLexer.hh in Headers */,
27A924D01D9C32E800086206 /* Path.hh in Headers */,
274D8245209A3A77008BB39F /* HeapDict.hh in Headers */,
274D8253209CF9B3008BB39F /* HeapValue.hh in Headers */,
Expand Down Expand Up @@ -1288,6 +1296,7 @@
27CA08431F6B0E9400FF8C71 /* Dict.cc in Sources */,
27867AF2211E27E5007BDA5F /* Doc.cc in Sources */,
27298E801C04E665000CFBA8 /* Encoder.cc in Sources */,
27A132812C73BF8C008E84FA /* FLEncoder.cc in Sources */,
27298E3C1C00F812000CFBA8 /* JSONConverter.cc in Sources */,
279AC53C1C097941002C80DB /* Value+Dump.cc in Sources */,
27FE87F31E53E43200C5CF3F /* JSONEncoder.cc in Sources */,
Expand Down
191 changes: 191 additions & 0 deletions Fleece/API_Impl/FLEncoder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//
// FLEncoder.cc
//
// Copyright 2016-Present Couchbase, Inc.
//
// Use of this software is governed by the Business Source License included
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
// in that file, in accordance with the Business Source License, use of this
// software will be governed by the Apache License, Version 2.0, included in
// the file licenses/APL2.txt.
//

#include "Fleece+ImplGlue.hh"
#include "Builder.hh"

#define ENCODER_DO(E, METHOD) (E)->do_([&](auto& e) {return e.METHOD;})

#define ENCODER_TRY(E, METHOD) return (E)->try_([&](auto e) { ENCODER_DO(e, METHOD); return true;})

FLEncoder FLEncoder_New(void) FLAPI {
return FLEncoder_NewWithOptions(kFLEncodeFleece, 0, true);
}

FLEncoder FLEncoder_NewWithOptions(FLEncoderFormat format,
size_t reserveSize, bool uniqueStrings) FLAPI
{
return new FLEncoderImpl(format, reserveSize, uniqueStrings);
}

FLEncoder FLEncoder_NewWritingToFile(FILE *outputFile, bool uniqueStrings) FLAPI {
return new FLEncoderImpl(outputFile, uniqueStrings);
}

void FLEncoder_Reset(FLEncoder e) FLAPI {
e->reset();
}

void FLEncoder_Free(FLEncoder FL_NULLABLE e) FLAPI {
delete e;
}

void FLEncoder_SetSharedKeys(FLEncoder e, FLSharedKeys FL_NULLABLE sk) FLAPI {
if (e->isFleece())
e->fleeceEncoder()->setSharedKeys(sk);
}

void FLEncoder_SuppressTrailer(FLEncoder e) FLAPI {
if (e->isFleece())
e->fleeceEncoder()->suppressTrailer();
}

void FLEncoder_Amend(FLEncoder e, FLSlice base, bool reuseStrings, bool externPointers) FLAPI {
if (e->isFleece() && base.size > 0) {
e->fleeceEncoder()->setBase(base, externPointers);
if(reuseStrings)
e->fleeceEncoder()->reuseBaseStrings();
}
}

FLSlice FLEncoder_GetBase(FLEncoder e) FLAPI {
if (e->isFleece())
return e->fleeceEncoder()->base();
return {};
}

size_t FLEncoder_GetNextWritePos(FLEncoder e) FLAPI {
if (e->isFleece())
return e->fleeceEncoder()->nextWritePos();
return 0;
}

size_t FLEncoder_BytesWritten(FLEncoder e) FLAPI {
return ENCODER_DO(e, bytesWritten());
}

intptr_t FLEncoder_LastValueWritten(FLEncoder e) FLAPI {
return e->isFleece() ? intptr_t(e->fleeceEncoder()->lastValueWritten()) : kFLNoWrittenValue;
}

bool FLEncoder_WriteValueAgain(FLEncoder e, intptr_t prewritten) FLAPI {
return e->isFleece() && e->fleeceEncoder()->writeValueAgain(Encoder::PreWrittenValue(prewritten));
}

bool FLEncoder_WriteNull(FLEncoder e) FLAPI {ENCODER_TRY(e, writeNull());}
bool FLEncoder_WriteUndefined(FLEncoder e) FLAPI {ENCODER_TRY(e, writeUndefined());}
bool FLEncoder_WriteBool(FLEncoder e, bool b) FLAPI {ENCODER_TRY(e, writeBool(b));}
bool FLEncoder_WriteInt(FLEncoder e, int64_t i) FLAPI {ENCODER_TRY(e, writeInt(i));}
bool FLEncoder_WriteUInt(FLEncoder e, uint64_t u) FLAPI {ENCODER_TRY(e, writeUInt(u));}
bool FLEncoder_WriteFloat(FLEncoder e, float f) FLAPI {ENCODER_TRY(e, writeFloat(f));}
bool FLEncoder_WriteDouble(FLEncoder e, double d) FLAPI {ENCODER_TRY(e, writeDouble(d));}
bool FLEncoder_WriteString(FLEncoder e, FLSlice s) FLAPI {ENCODER_TRY(e, writeString(s));}
bool FLEncoder_WriteDateString(FLEncoder e, FLTimestamp ts, bool asUTC)
FLAPI {ENCODER_TRY(e, writeDateString(ts,asUTC));}
bool FLEncoder_WriteData(FLEncoder e, FLSlice d) FLAPI {ENCODER_TRY(e, writeData(d));}
bool FLEncoder_WriteRaw(FLEncoder e, FLSlice r) FLAPI {ENCODER_TRY(e, writeRaw(r));}
bool FLEncoder_WriteValue(FLEncoder e, FLValue v) FLAPI {ENCODER_TRY(e, writeValue(v));}

bool FLEncoder_WriteFormatted(FLEncoder e, const char* format, ...) FLAPI {
va_list args;
va_start(args, format);
bool ok = FLEncoder_WriteFormattedArgs(e, format, args);
va_end(args);
return ok;
}

bool FLEncoder_WriteFormattedArgs(FLEncoder e, const char* format, va_list args) FLAPI {
return e->try_([&](auto impl) {
impl->do_([&](auto& enc) { builder::VEncode(enc, format, args); });
return true;
});
}

bool FLEncoder_BeginArray(FLEncoder e, size_t reserve) FLAPI {ENCODER_TRY(e, beginArray(reserve));}
bool FLEncoder_EndArray(FLEncoder e) FLAPI {ENCODER_TRY(e, endArray());}
bool FLEncoder_BeginDict(FLEncoder e, size_t reserve) FLAPI {ENCODER_TRY(e, beginDictionary(reserve));}
bool FLEncoder_WriteKey(FLEncoder e, FLSlice s) FLAPI {ENCODER_TRY(e, writeKey(s));}
bool FLEncoder_WriteKeyValue(FLEncoder e, FLValue key) FLAPI {ENCODER_TRY(e, writeKey(key));}
bool FLEncoder_EndDict(FLEncoder e) FLAPI {ENCODER_TRY(e, endDictionary());}

void FLJSONEncoder_NextDocument(FLEncoder e) FLAPI {
if (!e->isFleece())
e->jsonEncoder()->nextDocument();
}

bool FLEncoder_ConvertJSON(FLEncoder e, FLSlice json) FLAPI {
return e->try_([&](auto impl) { return impl->encodeJSON(json); });
}

FLError FLEncoder_GetError(FLEncoder e) FLAPI {
return e->errorCode;
}

const char* FL_NULLABLE FLEncoder_GetErrorMessage(FLEncoder e) FLAPI {
return e->errorMessage.empty() ? nullptr : e->errorMessage.c_str();
}

void FLEncoder_SetExtraInfo(FLEncoder e, void* FL_NULLABLE info) FLAPI {
e->extraInfo = info;
}

void* FL_NULLABLE FLEncoder_GetExtraInfo(FLEncoder e) FLAPI {
return e->extraInfo;
}

FLSliceResult FLEncoder_Snip(FLEncoder e) FLAPI {
if (e->isFleece())
return FLSliceResult(e->fleeceEncoder()->snip());
else
return {};
}

size_t FLEncoder_FinishItem(FLEncoder e) FLAPI {
if (e->isFleece())
return e->fleeceEncoder()->finishItem();
return 0;
}

FLDoc FL_NULLABLE FLEncoder_FinishDoc(FLEncoder e, FLError * FL_NULLABLE outError) FLAPI {
if (auto enc = e->fleeceEncoder()) {
if (!e->hasError()) {
try {
return retain(enc->finishDoc()); // finish() can throw
} catch (const std::exception &x) {
e->recordException(x);
}
}
} else {
e->errorCode = kFLUnsupported; // Doc class doesn't support JSON data
}
// Failure:
if (outError)
*outError = e->errorCode;
e->reset();
return nullptr;
}


FLSliceResult FLEncoder_Finish(FLEncoder e, FLError * FL_NULLABLE outError) FLAPI {
if (!e->hasError()) {
try {
return FLSliceResult(ENCODER_DO(e, finish())); // finish() can throw
} catch (const std::exception &x) {
e->recordException(x);
}
}
// Failure:
if (outError)
*outError = e->errorCode;
e->reset();
return {nullptr, 0};
}
Loading
Loading