-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '3.1.x' into test_parameters_3.1.7
- Loading branch information
Showing
148 changed files
with
15,594 additions
and
5,293 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// | ||
// Base.hh | ||
// | ||
// Copyright (c) 2019 Couchbase, Inc All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
#pragma once | ||
#include "cbl/CBLBase.h" | ||
#include "fleece/slice.hh" | ||
#include <algorithm> | ||
#include <functional> | ||
#include <cassert> | ||
#include <memory> | ||
#include <utility> | ||
|
||
#if DEBUG | ||
# include "cbl/CBLLog.h" | ||
#endif | ||
|
||
// VOLATILE API: Couchbase Lite C++ API is not finalized, and may change in | ||
// future releases. | ||
|
||
CBL_ASSUME_NONNULL_BEGIN | ||
|
||
static inline bool operator== (const CBLError &e1, const CBLError &e2) { | ||
if (e1.code != 0) | ||
return e1.domain == e2.domain && e1.code == e2.code; | ||
else | ||
return e2.code == 0; | ||
} | ||
|
||
namespace cbl { | ||
|
||
using slice = fleece::slice; | ||
using alloc_slice = fleece::alloc_slice; | ||
|
||
// Artificial base class of the C++ wrapper classes; just manages ref-counting. | ||
class RefCounted { | ||
protected: | ||
RefCounted() noexcept :_ref(nullptr) { } | ||
explicit RefCounted(CBLRefCounted* _cbl_nullable ref) noexcept :_ref(CBL_Retain(ref)) { } | ||
RefCounted(const RefCounted &other) noexcept :_ref(CBL_Retain(other._ref)) { } | ||
RefCounted(RefCounted &&other) noexcept :_ref(other._ref) {other._ref = nullptr;} | ||
~RefCounted() noexcept {CBL_Release(_ref);} | ||
|
||
RefCounted& operator= (const RefCounted &other) noexcept { | ||
CBL_Retain(other._ref); | ||
CBL_Release(_ref); | ||
_ref = other._ref; | ||
return *this; | ||
} | ||
|
||
RefCounted& operator= (RefCounted &&other) noexcept { | ||
if (other._ref != _ref) { | ||
CBL_Release(_ref); | ||
_ref = other._ref; | ||
other._ref = nullptr; | ||
} | ||
return *this; | ||
} | ||
|
||
void clear() {CBL_Release(_ref); _ref = nullptr;} | ||
bool valid() const {return _ref != nullptr;} \ | ||
explicit operator bool() const {return valid();} \ | ||
|
||
static std::string asString(FLSlice s) {return slice(s).asString();} | ||
static std::string asString(FLSliceResult &&s) {return alloc_slice(s).asString();} | ||
|
||
static void check(bool ok, CBLError &error) { | ||
if (!ok) { | ||
#if DEBUG | ||
alloc_slice message = CBLError_Message(&error); | ||
CBL_Log(kCBLLogDomainDatabase, kCBLLogError, "API returning error %d/%d: %.*s", | ||
error.domain, error.code, (int)message.size, (char*)message.buf); | ||
#endif | ||
throw error; | ||
} | ||
} | ||
|
||
CBLRefCounted* _cbl_nullable _ref; | ||
|
||
friend class Transaction; | ||
}; | ||
|
||
// Internal use only: Copy/move ctors and assignment ops that have to be declared in subclasses | ||
#define CBL_REFCOUNTED_WITHOUT_COPY_MOVE_BOILERPLATE(CLASS, SUPER, C_TYPE) \ | ||
public: \ | ||
CLASS() noexcept :SUPER() { } \ | ||
CLASS& operator=(std::nullptr_t) {clear(); return *this;} \ | ||
bool valid() const {return RefCounted::valid();} \ | ||
explicit operator bool() const {return valid();} \ | ||
bool operator==(const CLASS &other) const {return _ref == other._ref;} \ | ||
bool operator!=(const CLASS &other) const {return _ref != other._ref;} \ | ||
C_TYPE* _cbl_nullable ref() const {return (C_TYPE*)_ref;}\ | ||
protected: \ | ||
explicit CLASS(C_TYPE* _cbl_nullable ref) :SUPER((CBLRefCounted*)ref) { } | ||
|
||
#define CBL_REFCOUNTED_BOILERPLATE(CLASS, SUPER, C_TYPE) \ | ||
CBL_REFCOUNTED_WITHOUT_COPY_MOVE_BOILERPLATE(CLASS, SUPER, C_TYPE) \ | ||
public: \ | ||
CLASS(const CLASS &other) noexcept :SUPER(other) { } \ | ||
CLASS(CLASS &&other) noexcept :SUPER((SUPER&&)other) { } \ | ||
CLASS& operator=(const CLASS &other) noexcept {SUPER::operator=(other); return *this;} \ | ||
CLASS& operator=(CLASS &&other) noexcept {SUPER::operator=((SUPER&&)other); return *this;} | ||
|
||
/** A token representing a registered listener; instances are returned from the various | ||
methods that register listeners, such as \ref Database::addListener. | ||
When this object goes out of scope, the listener will be unregistered. | ||
@note ListenerToken is now allowed to copy. */ | ||
template <class... Args> | ||
class ListenerToken { | ||
public: | ||
using Callback = std::function<void(Args...)>; | ||
|
||
ListenerToken() =default; | ||
~ListenerToken() {CBLListener_Remove(_token);} | ||
|
||
ListenerToken(Callback cb) | ||
:_callback(new Callback(cb)) | ||
{ } | ||
|
||
ListenerToken(ListenerToken &&other) | ||
:_token(other._token), | ||
_callback(std::move(other._callback)) | ||
{other._token = nullptr;} | ||
|
||
ListenerToken& operator=(ListenerToken &&other) { | ||
CBLListener_Remove(_token); | ||
_token = other._token; | ||
other._token = nullptr; | ||
_callback = std::move(other._callback); | ||
return *this; | ||
} | ||
|
||
/** Unregisters the listener early, before it leaves scope. */ | ||
void remove() { | ||
CBLListener_Remove(_token); | ||
_token = nullptr; | ||
_callback = nullptr; | ||
} | ||
|
||
void* _cbl_nullable context() const {return _callback.get();} | ||
CBLListenerToken* _cbl_nullable token() const {return _token;} | ||
void setToken(CBLListenerToken* token) {assert(!_token); _token = token;} | ||
|
||
static void call(void* _cbl_nullable context, Args... args) { | ||
auto listener = (Callback*)context; | ||
(*listener)(args...); | ||
} | ||
|
||
private: | ||
CBLListenerToken* _cbl_nullable _token {nullptr}; | ||
std::shared_ptr<Callback> _callback; // Use shared_ptr instead of unique_ptr to allow to move | ||
|
||
ListenerToken(const ListenerToken&) =delete; | ||
ListenerToken& operator=(const ListenerToken &other) =delete; | ||
}; | ||
} | ||
|
||
CBL_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// | ||
// Blob.hh | ||
// | ||
// Copyright (c) 2019 Couchbase, Inc All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
#pragma once | ||
#include "cbl++/Document.hh" | ||
#include "cbl/CBLBlob.h" | ||
#include "fleece/Mutable.hh" | ||
#include <string> | ||
|
||
// VOLATILE API: Couchbase Lite C++ API is not finalized, and may change in | ||
// future releases. | ||
|
||
CBL_ASSUME_NONNULL_BEGIN | ||
|
||
namespace cbl { | ||
class BlobReadStream; | ||
class BlobWriteStream; | ||
|
||
/** A reference to a binary data blob associated with a document. | ||
A blob's persistent form is a special dictionary in the document properties. | ||
To work with a blob, you construct a Blob object with that dictionary. */ | ||
class Blob : protected RefCounted { | ||
public: | ||
/** Returns true if a dictionary in a document is a blob reference. | ||
@note This method tests whether the dictionary has a `@type` property, | ||
whose value is `"blob"`. */ | ||
static bool isBlob(fleece::Dict d) {return FLDict_IsBlob(d);} | ||
|
||
/** Creates a new blob, given its contents as a single block of data. | ||
@note The memory pointed to by `contents` is no longer needed after this call completes | ||
(it will have been written to the database.) | ||
@param contentType The MIME type (optional). | ||
@param contents The data's address and length. */ | ||
Blob(slice contentType, slice contents) { | ||
_ref = (CBLRefCounted*) CBLBlob_CreateWithData(contentType, contents); | ||
} | ||
|
||
/** Creates a new blob from the data written to a \ref CBLBlobWriteStream. | ||
@param contentType The MIME type (optional). | ||
@param writer The blob-writing stream the data was written to. */ | ||
inline Blob(slice contentType, BlobWriteStream& writer); | ||
|
||
/** Creates a Blob instance on an existing blob reference in a document or query result. | ||
@note If the dict argument is not actually a blob reference, this Blob object will be | ||
invalid; you can check that by calling its `valid` method or testing it with its | ||
`operator bool`. */ | ||
Blob(fleece::Dict d) | ||
:RefCounted((CBLRefCounted*) FLDict_GetBlob(d)) | ||
{ } | ||
|
||
/** Returns the length in bytes of a blob's content (from its `length` property). */ | ||
uint64_t length() const {return CBLBlob_Length(ref());} | ||
|
||
/** Returns a blob's MIME type, if its metadata has a `content_type` property. */ | ||
std::string contentType() const {return asString(CBLBlob_ContentType(ref()));} | ||
|
||
/** Returns the cryptographic digest of a blob's content (from its `digest` property). */ | ||
std::string digest() const {return asString(CBLBlob_Digest(ref()));} | ||
|
||
/** Returns a blob's metadata. This includes the `digest`, `length`, `content_type`, | ||
and `@type` properties, as well as any custom ones that may have been added. */ | ||
fleece::Dict properties() const {return CBLBlob_Properties(ref());} | ||
|
||
// Allows Blob to be assigned to mutable Dict/Array item, e.g. `dict["foo"] = blob` | ||
operator fleece::Dict() const {return properties();} | ||
|
||
/** Reads the blob's content into memory and returns them. */ | ||
alloc_slice loadContent() { | ||
CBLError error; | ||
fleece::alloc_slice content = CBLBlob_Content(ref(), &error); | ||
check(content.buf, error); | ||
return content; | ||
} | ||
|
||
/** Opens a stream for reading a blob's content. */ | ||
inline BlobReadStream* openContentStream(); | ||
|
||
protected: | ||
Blob(CBLRefCounted* r) :RefCounted(r) { } | ||
|
||
CBL_REFCOUNTED_BOILERPLATE(Blob, RefCounted, CBLBlob) | ||
}; | ||
|
||
/** A stream for writing a new blob to the database. */ | ||
class BlobReadStream { | ||
public: | ||
/** Opens a stream for reading a blob's content. */ | ||
BlobReadStream(Blob *blob) { | ||
CBLError error; | ||
_stream = CBLBlob_OpenContentStream(blob->ref(), &error); | ||
if (!_stream) throw error; | ||
} | ||
|
||
~BlobReadStream() { | ||
CBLBlobReader_Close(_stream); | ||
} | ||
|
||
/** Reads data from a blob. | ||
@param dst The address to copy the read data to. | ||
@param maxLength The maximum number of bytes to read. | ||
@return The actual number of bytes read; 0 if at EOF. */ | ||
size_t read(void *dst, size_t maxLength) { | ||
CBLError error; | ||
int bytesRead = CBLBlobReader_Read(_stream, dst, maxLength, &error); | ||
if (bytesRead < 0) | ||
throw error; | ||
return size_t(bytesRead); | ||
} | ||
|
||
private: | ||
CBLBlobReadStream* _cbl_nullable _stream {nullptr}; | ||
}; | ||
|
||
BlobReadStream* Blob::openContentStream() { | ||
return new BlobReadStream(this); | ||
} | ||
|
||
/** A stream for writing a new blob to the database. */ | ||
class BlobWriteStream { | ||
public: | ||
/** Create a stream to write a new blob to the database. */ | ||
BlobWriteStream(Database db) { | ||
CBLError error; | ||
_writer = CBLBlobWriter_Create(db.ref(), &error); | ||
if (!_writer) throw error; | ||
} | ||
|
||
~BlobWriteStream() { | ||
CBLBlobWriter_Close(_writer); | ||
} | ||
|
||
/** Writes data to a new blob. | ||
@param data The data to write. */ | ||
void write(fleece::slice data) { | ||
write(data.buf, data.size); | ||
} | ||
|
||
/** Writes data to a new blob. | ||
@param src The address of the data to write. | ||
@param length The length of the data to write. */ | ||
void write(const void *src, size_t length) { | ||
CBLError error; | ||
if (!CBLBlobWriter_Write(_writer, src, length, &error)) | ||
throw error; | ||
} | ||
|
||
private: | ||
friend class Blob; | ||
CBLBlobWriteStream* _cbl_nullable _writer {nullptr}; | ||
}; | ||
|
||
inline Blob::Blob(slice contentType, BlobWriteStream& writer) { | ||
_ref = (CBLRefCounted*) CBLBlob_CreateWithStream(contentType, writer._writer); | ||
writer._writer = nullptr; | ||
} | ||
} | ||
|
||
CBL_ASSUME_NONNULL_END |
Oops, something went wrong.