1 /*
2  * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
3  *
4  * Distributable under the terms of either the Apache License (Version 2.0) or
5  * the GNU Lesser General Public License, as specified in the COPYING file.
6  *
7  * Changes are Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
8 */
9 #include "CLucene/StdHeader.h"
10 #include "SegmentInfos.h"
11 
12 #include "CLucene/store/Directory.h"
13 #include "CLucene/util/Misc.h"
14 
15 CL_NS_USE(store)
CL_NS_USE(util)16 CL_NS_USE(util)
17 CL_NS_DEF(index)
18 
19 SegmentInfo::SegmentInfo(const QString& Name, const int32_t DocCount,
20     CL_NS(store)::Directory* Dir)
21     : docCount(DocCount)
22     , dir(Dir)
23 {
24     //Func - Constructor. Initialises SegmentInfo.
25     //Pre  - Name holds the unique name in the directory Dir
26     //       DocCount holds the number of documents in the segment
27     //       Dir holds the Directory where the segment resides
28     //Post - The instance has been created. name contains the duplicated string
29     //        Name. docCount = DocCount and dir references Dir
30     name = Name;
31 }
32 
~SegmentInfo()33 SegmentInfo::~SegmentInfo()
34 {
35 }
36 
SegmentInfos(bool _deleteMembers)37 SegmentInfos::SegmentInfos(bool _deleteMembers)
38     : deleteMembers(_deleteMembers)
39 {
40     //Func - Constructor
41     //Pre  - deleteMembers indicates if the instance to be created must delete
42     //       all SegmentInfo instances it manages when the instance is destroyed
43     //       or not true -> must delete, false may not delete
44     //Post - An instance of SegmentInfos has been created.
45 
46     //initialize counter to 0
47     counter = 0;
48     version = Misc::currentTimeMillis();
49 }
50 
~SegmentInfos()51 SegmentInfos::~SegmentInfos()
52 {
53     //Func - Destructor
54     //Pre  - true
55     //Post - The instance has been destroyed. Depending on the constructor used
56     //       the SegmentInfo instances that this instance managed have been
57     //       deleted or not.
58 
59     if (deleteMembers) {
60         segmentInfosType::iterator it;
61         for (it = infos.begin(); it != infos.end(); ++it)
62             _CLLDELETE(*it);
63     }
64     //Clear the list of SegmentInfo instances - make sure everything is deleted
65     infos.clear();
66 }
67 
info(int32_t i) const68 SegmentInfo* SegmentInfos::info(int32_t i) const
69 {
70     //Func - Returns a reference to the i-th SegmentInfo in the list.
71     //Pre  - i >= 0
72     //Post - A reference to the i-th SegmentInfo instance has been returned
73 
74     CND_PRECONDITION(i >= 0, "i contains negative number");
75 
76     //Get the i-th SegmentInfo instance
77     SegmentInfo *ret = infos.value(i, 0);
78 
79     //Condition check to see if the i-th SegmentInfo has been retrieved
80     CND_CONDITION(ret != NULL, "No SegmentInfo instance found");
81 
82     return ret;
83 }
84 
clearto(size_t _min)85 void SegmentInfos::clearto(size_t _min)
86 {
87     // Make sure we actually need to remove
88     if (infos.size() > _min) {
89         segmentInfosType::iterator itr;
90         segmentInfosType::iterator eitr = infos.end();
91         segmentInfosType::iterator bitr = infos.begin() + _min;
92 
93         for(itr = bitr; itr != eitr; ++itr)
94             _CLLDELETE((*itr));
95         infos.erase(bitr, eitr);
96     }
97 }
98 
add(SegmentInfo * info)99 void SegmentInfos::add(SegmentInfo* info)
100 {
101     infos.push_back(info);
102 }
103 
size() const104 int32_t SegmentInfos::size() const
105 {
106     return infos.size();
107 }
108 
read(Directory * directory)109 void SegmentInfos::read(Directory* directory)
110 {
111     //Func - Reads segments file that resides in directory.
112     //Pre  - directory contains a valid reference
113     //Post - The segments file has been read and for each segment found
114     //       a SegmentsInfo intance has been created and stored.
115 
116     //Open an IndexInput to the segments file and check if valid
117     IndexInput* input = directory->openInput(QLatin1String("segments"));
118     if (input) {
119         try {
120             int32_t format = input->readInt();
121             // file contains explicit format info
122             if (format < 0) {
123                 // check that it is a format we can understand
124                 if (format < FORMAT) {
125                     TCHAR err[30];
126                     _sntprintf(err, 30, _T("Unknown format version: %d"), format);
127                     _CLTHROWT(CL_ERR_Runtime, err);
128                 }
129                 // read version
130                 version = input->readLong();
131                 // read counter
132                 counter = input->readInt();
133             } else {
134                 // file is in old format without explicit format info
135                 counter = format;
136             }
137 
138             //Temporary variable for storing the name of the segment
139             char aname[CL_MAX_PATH] = { 0 };
140             TCHAR tname[CL_MAX_PATH] = { 0 };
141 
142             //read segmentInfos
143             for (int32_t i = input->readInt(); i > 0; --i) {
144                 // read the name of the segment
145                 input->readString(tname, CL_MAX_PATH);
146                 STRCPY_TtoA(aname, tname, CL_MAX_PATH);
147 
148                 //Instantiate a new SegmentInfo Instance
149                 SegmentInfo* si = _CLNEW SegmentInfo(QLatin1String(aname),
150                     input->readInt(), directory);
151 
152                 //Condition check to see if si points to an instance
153                 CND_CONDITION(si != NULL, "Memory allocation for si failed")	;
154 
155                 //store SegmentInfo si
156                 infos.push_back(si);
157             }
158 
159             if (format >= 0) {
160                 // in old format the version number may be at the end of the file
161                 if (input->getFilePointer() >= input->length()) {
162                     // old file format without version number
163                     version = Misc::currentTimeMillis();
164                 } else {
165                     // read version
166                     version = input->readLong();
167                 }
168             }
169         } _CLFINALLY (
170             //destroy the inputStream input. The destructor of IndexInput will
171             //also close the Inputstream input
172             _CLDELETE(input);
173         );
174     }
175 }
176 
write(Directory * directory)177 void SegmentInfos::write(Directory* directory)
178 {
179     //Func - Writes a new segments file based upon the SegmentInfo instances it manages
180     //Pre  - directory is a valid reference to a Directory
181     //Post - The new segment has been written to disk
182 
183     //Open an IndexOutput to the segments file and check if valid
184     IndexOutput* output = directory->createOutput(QLatin1String("segments.new"));
185     if (output) {
186         try {
187             // write FORMAT
188             output->writeInt(FORMAT);
189             // every write changes the index
190             output->writeLong(++version);
191              // Write the counter
192             output->writeInt(counter);
193 
194             // Write the number of SegmentInfo Instances which is equal to the number
195             // of segments in directory as each SegmentInfo manages a single segment
196             output->writeInt(infos.size());
197 
198             //temporary value for wide segment name
199             TCHAR tname[CL_MAX_PATH];
200 
201             //Iterate through all the SegmentInfo instances
202             for (uint32_t i = 0; i < infos.size(); ++i) {
203                 //Retrieve the SegmentInfo
204                 SegmentInfo *si = infos.value(i, 0);
205                 //Condition check to see if si has been retrieved
206                 CND_CONDITION(si != NULL, "No SegmentInfo instance found");
207 
208                 //Write the name of the current segment
209                 int32_t count = si->name.toWCharArray(tname);
210                 tname[count] = '\0';
211                 output->writeString(tname, _tcslen(tname));
212 
213                 //Write the number of documents in the segment
214                 output->writeInt(si->docCount);
215             }
216         } _CLFINALLY(
217             output->close();
218             _CLDELETE(output);
219         );
220 
221         // install new segment info
222         directory->renameFile(QLatin1String("segments.new"),
223             QLatin1String("segments"));
224     }
225 }
226 
227 
readCurrentVersion(Directory * directory)228 int64_t SegmentInfos::readCurrentVersion(Directory* directory)
229 {
230     int32_t format = 0;
231     int64_t version = 0;
232     IndexInput* input = directory->openInput(QLatin1String("segments"));
233     try {
234         format = input->readInt();
235         if (format < 0){
236             if (format < FORMAT) {
237                 TCHAR err[30];
238                 _sntprintf(err, 30, _T("Unknown format version: %d"), format);
239                 _CLTHROWT(CL_ERR_Runtime, err);
240             }
241             // read version
242             version = input->readLong();
243         }
244     } _CLFINALLY (
245         input->close();
246         _CLDELETE(input);
247     );
248 
249     if (format < 0)
250         return version;
251 
252     // We cannot be sure about the format of the file. Therefore we have to
253     // read the whole file and cannot simply seek to the version entry.
254     SegmentInfos segmentInfos;
255     segmentInfos.read(directory);
256     return segmentInfos.getVersion();
257 }
258 
259 CL_NS_END
260