/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ using System; using RAMOutputStream = Lucene.Net.Store.RAMOutputStream; using ArrayUtil = Lucene.Net.Util.ArrayUtil; namespace Lucene.Net.Index { /// This is a DocFieldConsumer that writes stored fields. sealed class StoredFieldsWriter { private void InitBlock() { docFreeList = new PerDoc[1]; } internal FieldsWriter fieldsWriter; internal DocumentsWriter docWriter; internal FieldInfos fieldInfos; internal int lastDocID; internal PerDoc[] docFreeList; internal int freeCount; public StoredFieldsWriter(DocumentsWriter docWriter, FieldInfos fieldInfos) { InitBlock(); this.docWriter = docWriter; this.fieldInfos = fieldInfos; } public StoredFieldsWriterPerThread AddThread(DocumentsWriter.DocState docState) { return new StoredFieldsWriterPerThread(docState, this); } public void Flush(SegmentWriteState state) { lock (this) { if (state.numDocsInStore > 0) { // It's possible that all documents seen in this segment // hit non-aborting exceptions, in which case we will // not have yet init'd the FieldsWriter: InitFieldsWriter(); // Fill fdx file to include any final docs that we // skipped because they hit non-aborting exceptions Fill(state.numDocsInStore - docWriter.DocStoreOffset); } if (fieldsWriter != null) fieldsWriter.Flush(); } } private void InitFieldsWriter() { if (fieldsWriter == null) { System.String docStoreSegment = docWriter.DocStoreSegment; if (docStoreSegment != null) { System.Diagnostics.Debug.Assert(docStoreSegment != null); fieldsWriter = new FieldsWriter(docWriter.directory, docStoreSegment, fieldInfos); docWriter.AddOpenFile(docStoreSegment + "." + IndexFileNames.FIELDS_EXTENSION); docWriter.AddOpenFile(docStoreSegment + "." + IndexFileNames.FIELDS_INDEX_EXTENSION); lastDocID = 0; } } } public void CloseDocStore(SegmentWriteState state) { lock (this) { int inc = state.numDocsInStore - lastDocID; if (inc > 0) { InitFieldsWriter(); Fill(state.numDocsInStore - docWriter.DocStoreOffset); } if (fieldsWriter != null) { fieldsWriter.Dispose(); fieldsWriter = null; lastDocID = 0; System.Diagnostics.Debug.Assert(state.docStoreSegmentName != null); state.flushedFiles.Add(state.docStoreSegmentName + "." + IndexFileNames.FIELDS_EXTENSION); state.flushedFiles.Add(state.docStoreSegmentName + "." + IndexFileNames.FIELDS_INDEX_EXTENSION); state.docWriter.RemoveOpenFile(state.docStoreSegmentName + "." + IndexFileNames.FIELDS_EXTENSION); state.docWriter.RemoveOpenFile(state.docStoreSegmentName + "." + IndexFileNames.FIELDS_INDEX_EXTENSION); System.String fileName = state.docStoreSegmentName + "." + IndexFileNames.FIELDS_INDEX_EXTENSION; if (4 + ((long) state.numDocsInStore) * 8 != state.directory.FileLength(fileName)) throw new System.SystemException("after flush: fdx size mismatch: " + state.numDocsInStore + " docs vs " + state.directory.FileLength(fileName) + " length in bytes of " + fileName + " file exists?=" + state.directory.FileExists(fileName)); } } } internal int allocCount; internal PerDoc GetPerDoc() { lock (this) { if (freeCount == 0) { allocCount++; if (allocCount > docFreeList.Length) { // Grow our free list up front to make sure we have // enough space to recycle all outstanding PerDoc // instances System.Diagnostics.Debug.Assert(allocCount == 1 + docFreeList.Length); docFreeList = new PerDoc[ArrayUtil.GetNextSize(allocCount)]; } return new PerDoc(this); } else return docFreeList[--freeCount]; } } internal void Abort() { lock (this) { if (fieldsWriter != null) { try { fieldsWriter.Dispose(); } catch (System.Exception) { } fieldsWriter = null; lastDocID = 0; } } } /// Fills in any hole in the docIDs internal void Fill(int docID) { int docStoreOffset = docWriter.DocStoreOffset; // We must "catch up" for all docs before us // that had no stored fields: int end = docID + docStoreOffset; while (lastDocID < end) { fieldsWriter.SkipDocument(); lastDocID++; } } internal void FinishDocument(PerDoc perDoc) { lock (this) { System.Diagnostics.Debug.Assert(docWriter.writer.TestPoint("StoredFieldsWriter.finishDocument start")); InitFieldsWriter(); Fill(perDoc.docID); // Append stored fields to the real FieldsWriter: fieldsWriter.FlushDocument(perDoc.numStoredFields, perDoc.fdt); lastDocID++; perDoc.Reset(); Free(perDoc); System.Diagnostics.Debug.Assert(docWriter.writer.TestPoint("StoredFieldsWriter.finishDocument end")); } } public bool FreeRAM() { return false; } internal void Free(PerDoc perDoc) { lock (this) { System.Diagnostics.Debug.Assert(freeCount < docFreeList.Length); System.Diagnostics.Debug.Assert(0 == perDoc.numStoredFields); System.Diagnostics.Debug.Assert(0 == perDoc.fdt.Length); System.Diagnostics.Debug.Assert(0 == perDoc.fdt.FilePointer); docFreeList[freeCount++] = perDoc; } } internal class PerDoc:DocumentsWriter.DocWriter { public PerDoc(StoredFieldsWriter enclosingInstance) { InitBlock(enclosingInstance); } private void InitBlock(StoredFieldsWriter enclosingInstance) { this.enclosingInstance = enclosingInstance; buffer = enclosingInstance.docWriter.NewPerDocBuffer(); fdt = new RAMOutputStream(buffer); } private StoredFieldsWriter enclosingInstance; public StoredFieldsWriter Enclosing_Instance { get { return enclosingInstance; } } internal DocumentsWriter.PerDocBuffer buffer ; internal RAMOutputStream fdt; internal int numStoredFields; internal void Reset() { fdt.Reset(); buffer.Recycle(); numStoredFields = 0; } public override void Abort() { Reset(); Enclosing_Instance.Free(this); } public override long SizeInBytes() { return buffer.SizeInBytes; } public override void Finish() { Enclosing_Instance.FinishDocument(this); } } } }