예제 #1
0
        private unsafe void SaveProperties(IDictionary <string, object> props, string docID)
        {
            Db.InBatch(() =>
            {
                var tricky =
                    (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db.c4db, docID, true, err));
                var put = new C4DocPutRequest {
                    docID        = tricky->docID,
                    history      = &tricky->revID,
                    historyCount = 1,
                    save         = true
                };

                var enc = Native.c4db_getSharedFleeceEncoder(Db.c4db);
                props.FLEncode(enc);
                var body = NativeRaw.FLEncoder_Finish(enc, null);
                put.body = (C4Slice)body;

                LiteCoreBridge.Check(err =>
                {
                    var localPut = put;
                    var retVal   = Native.c4doc_put(Db.c4db, &localPut, null, err);
                    Native.FLSliceResult_Free(body);
                    return(retVal);
                });
            });
        }
        public override void Flush()
        {
            FLError err;

            Result = NativeRaw.FLEncoder_Finish(_encoder, &err);
            if (Result.buf == null)
            {
                throw new LiteCoreException(new C4Error(err));
            }
        }
예제 #3
0
        public void TestLegacyProperties()
        {
            RunTestVariants(() =>
            {
                Native.c4doc_isOldMetaProperty("_attachments").Should().BeTrue();
                Native.c4doc_isOldMetaProperty("@type").Should().BeFalse();

                var enc = Native.c4db_getSharedFleeceEncoder(Db);
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    Native.FLEncoder_BeginDict(enc, 2);
                    Native.FLEncoder_WriteKey(enc, "@type");
                    Native.FLEncoder_WriteString(enc, "blob");
                    Native.FLEncoder_WriteKey(enc, "digest");
                    Native.FLEncoder_WriteString(enc, String.Empty);
                    Native.FLEncoder_EndDict(enc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                var result = NativeRaw.FLEncoder_Finish(enc, null);
                ((long)result.buf).Should().NotBe(0);
                try {
                    var val = NativeRaw.FLValue_FromTrustedData((FLSlice)result);
                    var d   = Native.FLValue_AsDict(val);
                    ((long)d).Should().NotBe(0);

                    var key    = Native.c4db_initFLDictKey(Db, "@type");
                    var keyStr = Native.FLDictKey_GetString(&key);
                    keyStr.Should().Be("@type");
                    var testVal = Native.FLDict_GetWithKey(d, &key);

                    Native.FLValue_AsString(testVal).Should().Be("blob");
                    Native.c4doc_dictContainsBlobs(d, Native.c4db_getFLSharedKeys(Db)).Should().BeTrue();
                } finally {
                    Native.FLSliceResult_Free(result);
                }

                enc = Native.c4db_getSharedFleeceEncoder(Db);
                Native.FLEncoder_BeginDict(enc, 0);
                Native.FLEncoder_EndDict(enc);
                result = NativeRaw.FLEncoder_Finish(enc, null);
                ((long)result.buf).Should().NotBe(0);
                try {
                    var val = NativeRaw.FLValue_FromTrustedData((FLSlice)result);
                    var d   = Native.FLValue_AsDict(val);
                    ((long)d).Should().NotBe(0);

                    Native.c4doc_dictContainsBlobs(d, Native.c4db_getFLSharedKeys(Db)).Should().BeFalse();
                } finally {
                    Native.FLSliceResult_Free(result);
                }
            });
        }
예제 #4
0
        static Test()
        {
            var enc = Native.FLEncoder_New();

            Native.FLEncoder_BeginDict(enc, 1);
            Native.FLEncoder_WriteKey(enc, "answer");
            Native.FLEncoder_WriteInt(enc, 42);
            Native.FLEncoder_EndDict(enc);
            var result = NativeRaw.FLEncoder_Finish(enc, null);

            FleeceBody = result;
        }
        internal void AddPersonInState(string docID, string state, string firstName = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            C4Error error;
            var     enc = Native.c4db_getSharedFleeceEncoder(Db);

            try {
                Native.FLEncoder_BeginDict(enc, 3);
                Native.FLEncoder_WriteKey(enc, "custom");
                Native.FLEncoder_WriteBool(enc, true);
                if (!string.IsNullOrEmpty(firstName))
                {
                    Native.FLEncoder_WriteKey(enc, "name");
                    Native.FLEncoder_BeginDict(enc, 2);
                    Native.FLEncoder_WriteKey(enc, "first");
                    Native.FLEncoder_WriteString(enc, firstName);
                    Native.FLEncoder_WriteKey(enc, "last");
                    Native.FLEncoder_WriteString(enc, "lastname");
                    Native.FLEncoder_EndDict(enc);
                }

                Native.FLEncoder_WriteKey(enc, "contact");
                Native.FLEncoder_BeginDict(enc, 1);
                Native.FLEncoder_WriteKey(enc, "address");
                Native.FLEncoder_BeginDict(enc, 1);
                Native.FLEncoder_WriteKey(enc, "state");
                Native.FLEncoder_WriteString(enc, state);
                Native.FLEncoder_EndDict(enc);
                Native.FLEncoder_EndDict(enc);
                Native.FLEncoder_EndDict(enc);

                // Save document:
                FLSliceResult body = NativeRaw.FLEncoder_Finish(enc, null);
                //REQUIRE(body.buf);
                using (var docID_ = new C4String(docID)){
                    var rq = new C4DocPutRequest {
                        allowConflict = false,
                        docID         = docID_.AsFLSlice(),
                        allocedBody   = body,
                        save          = true
                    };

                    C4Document *doc;
                    doc = Native.c4doc_put(Db, &rq, null, &error);
                    ((long)doc).Should().NotBe(0, "because otherwise the put failed");
                    Native.c4doc_release(doc);
                    Native.FLSliceResult_Release(body);
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
예제 #6
0
 static Test()
 {
     #if NETCOREAPP2_0
     Couchbase.Lite.Support.NetDesktop.Activate();
     #endif
     var enc = Native.FLEncoder_New();
     Native.FLEncoder_BeginDict(enc, 1);
     Native.FLEncoder_WriteKey(enc, "answer");
     Native.FLEncoder_WriteInt(enc, 42);
     Native.FLEncoder_EndDict(enc);
     var result = NativeRaw.FLEncoder_Finish(enc, null);
     FleeceBody = (C4Slice)result;
 }
예제 #7
0
 static Test()
 {
     #if NETCOREAPP2_1 && !CBL_NO_VERSION_CHECK
     Couchbase.Lite.Support.NetDesktop.CheckVersion();
     #endif
     var enc = Native.FLEncoder_New();
     Native.FLEncoder_BeginDict(enc, 1);
     Native.FLEncoder_WriteKey(enc, "ans*wer");
     Native.FLEncoder_WriteInt(enc, 42);
     Native.FLEncoder_EndDict(enc);
     var result = NativeRaw.FLEncoder_Finish(enc, null);
     FleeceBody = (FLSlice)result;
 }
예제 #8
0
 static Test()
 {
     #if NETCOREAPP1_0
     LoadDLL();
     #endif
     var enc = Native.FLEncoder_New();
     Native.FLEncoder_BeginDict(enc, 1);
     Native.FLEncoder_WriteKey(enc, "answer");
     Native.FLEncoder_WriteInt(enc, 42);
     Native.FLEncoder_EndDict(enc);
     var result = NativeRaw.FLEncoder_Finish(enc, null);
     FleeceBody = result;
 }
예제 #9
0
        public FLSliceResult Encode()
        {
            var enc = Native.FLEncoder_New();

            FLEncode(enc);

            FLError error;
            var     result = NativeRaw.FLEncoder_Finish(enc, &error);

            if (result.buf == null)
            {
                throw new CouchbaseFleeceException(error);
            }

            return(result);
        }
예제 #10
0
        public override MutableDocument ToMutable() => new MutableDocument(this); // MutableDocument constructor is different, so this override is needed

        internal override byte[] Encode()
        {
            Debug.Assert(Database != null);

            var body = new FLSliceResult();

            Database.ThreadSafety.DoLocked(() =>
            {
                FLEncoder *encoder = null;
                try {
                    encoder = Database.SharedEncoder;
                } catch (Exception) {
                    body = new FLSliceResult(null, 0UL);
                }

                #if CBL_LINQ
                if (_model != null)
                {
                    return((FLSlice)EncodeModel(encoder));
                }
                #endif

                var handle = GCHandle.Alloc(this);
                Native.FLEncoder_SetExtraInfo(encoder, (void *)GCHandle.ToIntPtr(handle));

                try {
                    _dict.FLEncode(encoder);
                } catch (Exception) {
                    Native.FLEncoder_Reset(encoder);
                    throw;
                } finally {
                    handle.Free();
                }

                FLError err;
                body = NativeRaw.FLEncoder_Finish(encoder, &err);
                if (body.buf == null)
                {
                    throw new CouchbaseFleeceException(err);
                }
            });

            var retVal = ((FLSlice)body).ToArrayFast();
            Native.FLSliceResult_Release(body);
            return(retVal);
        }
예제 #11
0
        public FLSliceResult EncodeDelta()
        {
            var enc = Native.FLEncoder_New();

            NativeRaw.FLEncoder_MakeDelta(enc, Context.Data, true);
            FLEncode(enc);

            FLError error;
            var     result = NativeRaw.FLEncoder_Finish(enc, &error);

            if (result.buf == null)
            {
                throw new LiteCoreException(new C4Error(error));
            }

            return(result);
        }
        internal override byte[] Encode()
        {
            Debug.Assert(Database != null);

            var body = new FLSliceResult();

            Database.ThreadSafety.DoLocked(() =>
            {
                var encoder = Database.SharedEncoder;

                #if CBL_LINQ
                if (_model != null)
                {
                    return((FLSlice)EncodeModel(encoder));
                }
                #endif

                var guid = Guid.NewGuid();
                _NativeCacheMap[guid] = this;
                Native.FLEncoder_SetExtraInfo(encoder, &guid);

                try {
                    _dict.FLEncode(encoder);
                } catch (Exception) {
                    Native.FLEncoder_Reset(encoder);
                    throw;
                } finally {
                    _NativeCacheMap.Remove(guid);
                }

                FLError err;
                body = NativeRaw.FLEncoder_Finish(encoder, &err);
                if (body.buf == null)
                {
                    throw new LiteCoreException(new C4Error(err));
                }
            });

            var retVal = ((C4Slice)body).ToArrayFast();
            Native.FLSliceResult_Free(body);
            return(retVal);
        }
예제 #13
0
        protected uint ImportJSONFile(string path, string idPrefix, TimeSpan timeout, bool verbose)
        {
            WriteLine($"Reading {path} ...");
            var st = Stopwatch.StartNew();

#if WINDOWS_UWP
            var url  = $"ms-appx:///Assets/{path}";
            var file = Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri(url))
                       .AsTask()
                       .ConfigureAwait(false)
                       .GetAwaiter()
                       .GetResult();

            var buffer = Windows.Storage.FileIO.ReadBufferAsync(file).AsTask().ConfigureAwait(false).GetAwaiter()
                         .GetResult();
            var jsonData = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer);
#elif __ANDROID__
            var    ctx = global::Couchbase.Lite.Tests.Android.MainActivity.ActivityContext;
            byte[] jsonData;
            using (var stream = ctx.Assets.Open(path))
                using (var ms = new MemoryStream()) {
                    stream.CopyTo(ms);
                    jsonData = ms.ToArray();
                }
#elif __IOS__
            var    bundlePath = ios::Foundation.NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(path), Path.GetExtension(path));
            byte[] jsonData;
            using (var stream = File.Open(bundlePath, FileMode.Open, FileAccess.Read))
                using (var ms = new MemoryStream()) {
                    stream.CopyTo(ms);
                    jsonData = ms.ToArray();
                }
#else
            var jsonData = File.ReadAllBytes(path);
#endif

            FLError       error;
            FLSliceResult fleeceData;
            fixed(byte *jsonData_ = jsonData)
            {
                fleeceData = NativeRaw.FLData_ConvertJSON(new FLSlice(jsonData_, (ulong)jsonData.Length), &error);
            }

            ((long)fleeceData.buf).Should().NotBe(0, "because otherwise the conversion failed");
            var root = Native.FLValue_AsArray(NativeRaw.FLValue_FromTrustedData((FLSlice)fleeceData));
            ((long)root).Should().NotBe(0, "because otherwise the value is not of the expected type");

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                FLArrayIterator iter;
                FLValue *       item;
                uint            numDocs = 0;
                for (Native.FLArrayIterator_Begin(root, &iter);
                     null != (item = Native.FLArrayIterator_GetValue(&iter));
                     Native.FLArrayIterator_Next(&iter))
                {
                    var docID = idPrefix != null ? $"{idPrefix}{numDocs + 1:D7}" : $"doc{numDocs + 1:D7}";
                    var enc   = Native.c4db_getSharedFleeceEncoder(Db);
                    Native.FLEncoder_WriteValue(enc, item);
                    var body = NativeRaw.FLEncoder_Finish(enc, &error);

                    var rq = new C4DocPutRequest {
                        docID = C4Slice.Allocate(docID),
                        body  = (C4Slice)body,
                        save  = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err =>
                    {
                        var localPut = rq;
                        return(Native.c4doc_put(Db, &localPut, null, err));
                    });

                    Native.c4doc_free(doc);
                    Native.FLSliceResult_Free(body);
                    C4Slice.Free(rq.docID);
                    ++numDocs;
                    if ((numDocs % 1000) == 0 && st.Elapsed > timeout)
                    {
                        WriteLine($"WARNING: Stopping JSON import after {st.Elapsed}");
                        return(numDocs);
                    }
                    if (verbose && (numDocs % 100000) == 0)
                    {
                        WriteLine($"{numDocs}  ");
                    }
                }

                if (verbose)
                {
                    st.PrintReport("Importing", numDocs, "doc", _output);
                }

                return(numDocs);
            }
            finally {
                Native.FLSliceResult_Free(fleeceData);
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
예제 #14
0
        // Read a file that contains a JSON document per line. Every line becomes a document.
        protected uint ImportJSONLines(string path, TimeSpan timeout, bool verbose)
        {
            if (verbose)
            {
                WriteLine($"Reading {path}...");
            }

            var  st      = Stopwatch.StartNew();
            uint numDocs = 0;

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var encoder = Native.FLEncoder_New();
                ReadFileByLines(path, line => {
                    FLError error;
                    NativeRaw.FLEncoder_ConvertJSON(encoder, line);
                    var body = NativeRaw.FLEncoder_Finish(encoder, &error);
                    ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed");
                    Native.FLEncoder_Reset(encoder);

                    var docID = (numDocs + 1).ToString("D7");

                    // Save document:
                    using (var docID_ = new C4String(docID)) {
                        var rq = new C4DocPutRequest {
                            docID = docID_.AsC4Slice(),
                            body  = (C4Slice)body,
                            save  = true
                        };
                        var doc = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            return(Native.c4doc_put(Db, &localRq, null, err));
                        });
                        Native.c4doc_free(doc);
                    }

                    Native.FLSliceResult_Free(body);
                    ++numDocs;
                    if (numDocs % 1000 == 0 && st.Elapsed >= timeout)
                    {
                        Console.Write($"Stopping JSON import after {st.Elapsed.TotalSeconds:F3} sec ");
                        return(false);
                    }

                    if (verbose && numDocs % 10000 == 0)
                    {
                        Console.Write($"{numDocs} ");
                    }

                    return(true);
                });

                if (verbose)
                {
                    WriteLine("Committing...");
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }

            if (verbose)
            {
                st.PrintReport("Importing", numDocs, "doc", _output);
            }

            return(numDocs);
        }
예제 #15
0
        private uint InsertDocs(FLArray *docs)
        {
            var typeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Type"), true);
            var idKey      = NativeRaw.FLDictKey_Init(FLSlice.Constant("Persistent ID"), true);
            var nameKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Name"), true);
            var albumKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Album"), true);
            var artistKey  = NativeRaw.FLDictKey_Init(FLSlice.Constant("Artist"), true);
            var timeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Total Time"), true);
            var genreKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Genre"), true);
            var yearKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Year"), true);
            var trackNoKey = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Number"), true);
            var compKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Compilation"), true);

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var             enc = Native.FLEncoder_New();
                FLArrayIterator iter;
                Native.FLArrayIterator_Begin(docs, &iter);
                uint numDocs = 0;
                while (Native.FLArrayIterator_Next(&iter))
                {
                    // Check that track is correct type:
                    var track     = Native.FLValue_AsDict(Native.FLArrayIterator_GetValue(&iter));
                    var trackType = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &typeKey));
                    if (!trackType.Equals(FLSlice.Constant("File")) && !trackType.Equals(FLSlice.Constant("Remote")))
                    {
                        continue;
                    }

                    var trackID = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &idKey));
                    ((long)trackID.buf).Should().NotBe(0, "because otherwise the data was not read correctly");

                    // Encode doc body:
                    Native.FLEncoder_BeginDict(enc, 0);
                    CopyValue(track, &nameKey, enc).Should().BeTrue("because otherwise the copy failed");
                    CopyValue(track, &albumKey, enc);
                    CopyValue(track, &artistKey, enc);
                    CopyValue(track, &timeKey, enc);
                    CopyValue(track, &genreKey, enc);
                    CopyValue(track, &yearKey, enc);
                    CopyValue(track, &trackNoKey, enc);
                    CopyValue(track, &compKey, enc);
                    Native.FLEncoder_EndDict(enc);
                    FLError err;
                    var     body = NativeRaw.FLEncoder_Finish(enc, &err);
                    body.Should().NotBeNull("because otherwise the encoding process failed");
                    Native.FLEncoder_Reset(enc);

                    // Save Document:
                    var rq = new C4DocPutRequest();
                    rq.docID = (C4Slice)trackID;
                    rq.body  = body;
                    rq.save  = true;
                    var doc = (C4Document *)LiteCoreBridge.Check(c4err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, c4err));
                    });

                    Native.c4doc_free(doc);
                    ++numDocs;
                }

                Native.FLEncoder_Free(enc);
                return(numDocs);
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
        private void IndexViews()
        {
            var nameKey    = Native.FLDictKey_Init("Name", true);
            var albumKey   = Native.FLDictKey_Init("Album", true);
            var artistKey  = Native.FLDictKey_Init("Artist", true);
            var timeKey    = Native.FLDictKey_Init("Total Time", true);
            var trackNoKey = Native.FLDictKey_Init("Track Number", true);
            var compKey    = Native.FLDictKey_Init("Compilation", true);

            var enc = Native.FLEncoder_New();
            var key = Native.c4key_new();

            C4Error error;

            if (_artistsView == null)
            {
                var config = Native.c4db_getConfig(Db);
                _artistsView = (C4View *)LiteCoreBridge.Check(err => Native.c4view_open(Db, null, "Artists", "1",
                                                                                        Native.c4db_getConfig(Db), err));
            }

            if (_albumsView == null)
            {
                _albumsView = (C4View *)LiteCoreBridge.Check(err => Native.c4view_open(Db, null, "Albums", "1",
                                                                                       Native.c4db_getConfig(Db), err));
            }

            var views   = new[] { _artistsView, _albumsView };
            var indexer = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, views, err));
            var e       = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(indexer, err));

            while (Native.c4enum_next(e, &error))
            {
                var doc  = Native.c4enum_getDocument(e, &error);
                var body = Native.FLValue_AsDict(NativeRaw.FLValue_FromTrustedData((FLSlice)doc->selectedRev.body));
                ((long)body).Should().NotBe(0, "because otherwise the data got corrupted somehow");

                FLSlice artist;
                if (Native.FLValue_AsBool(Native.FLDict_GetWithKey(body, &compKey)))
                {
                    artist = FLSlice.Constant("-Compilations-");
                }
                else
                {
                    artist = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(body, &artistKey));
                }

                var name    = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(body, &nameKey));
                var album   = Native.FLValue_AsString(Native.FLDict_GetWithKey(body, &albumKey));
                var trackNo = Native.FLValue_AsInt(Native.FLDict_GetWithKey(body, &trackNoKey));
                var time    = Native.FLDict_GetWithKey(body, &timeKey);

                // Generate Value:
                Native.FLEncoder_WriteValue(enc, time);
                FLError flError;
                var     fval = NativeRaw.FLEncoder_Finish(enc, &flError);
                Native.FLEncoder_Reset(enc);
                ((long)fval.buf).Should().NotBe(0, "because otherwise the encoding failed");
                var value = (C4Slice)fval;

                // Emit to artists view:
                uint nKeys = 0;
                if (!artist.Equals(FLSlice.Null) && !name.Equals(FLSlice.Null))
                {
                    nKeys = 1;
                    // Generate key:
                    Native.c4key_beginArray(key);
                    NativeRaw.c4key_addString(key, (C4Slice)artist);
                    if (album != null)
                    {
                        Native.c4key_addString(key, album);
                    }
                    else
                    {
                        Native.c4key_addNull(key);
                    }

                    Native.c4key_addNumber(key, trackNo);
                    NativeRaw.c4key_addString(key, (C4Slice)name);
                    Native.c4key_addNumber(key, 1.0);
                    Native.c4key_endArray(key);
                }

                Native.c4indexer_emit(indexer, doc, 0, nKeys, new[] { key }, new[] { value }, &error).Should()
                .BeTrue("because otherwise the emit to the artists view failed");
                Native.c4key_reset(key);

                // Emit to albums view:
                nKeys = 0;
                if (album != null)
                {
                    nKeys = 1;
                    Native.c4key_beginArray(key);
                    Native.c4key_addString(key, album);
                    if (!artist.Equals(FLSlice.Null))
                    {
                        NativeRaw.c4key_addString(key, (C4Slice)artist);
                    }
                    else
                    {
                        Native.c4key_addNull(key);
                    }

                    Native.c4key_addNumber(key, trackNo);
                    if (name.buf == null)
                    {
                        name = FLSlice.Constant("");
                    }

                    NativeRaw.c4key_addString(key, (C4Slice)name);
                    Native.c4key_addNumber(key, 1.0);
                    Native.c4key_endArray(key);
                }

                Native.c4indexer_emit(indexer, doc, 1, nKeys, new[] { key }, new[] { value }, &error).Should()
                .BeTrue("because otherwise the emit to the artists view failed");
                Native.c4key_reset(key);

                Native.FLSliceResult_Free(fval);
                Native.c4doc_free(doc);
            }

            Native.c4enum_free(e);
            error.Code.Should().Be(0, "because otherwise an error occurred");
            Native.c4indexer_end(indexer, true, &error).Should().BeTrue("because otherwise the indexer failed to end");
            Native.FLEncoder_Free(enc);
            Native.c4key_free(key);
        }