write phrase index logger in progress
[platform/upstream/libpinyin.git] / src / storage / phrase_index.cpp
1 /* 
2  *  libpinyin
3  *  Library to deal with pinyin.
4  *  
5  *  Copyright (C) 2006-2007 Peng Wu
6  *  
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  *  
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22 #include "phrase_index.h"
23
24 using namespace pinyin;
25
26 bool PhraseItem::set_n_pronunciation(guint8 n_prouns){
27     m_chunk.set_content(sizeof(guint8), &n_prouns, sizeof(guint8));
28     return true;
29 }
30
31 bool PhraseItem::get_nth_pronunciation(size_t index, PinyinKey * pinyin, guint32 & freq){
32     guint8 phrase_length = get_phrase_length();
33     table_offset_t offset = phrase_item_header + phrase_length * sizeof( utf16_t) + index * ( phrase_length * sizeof (PinyinKey) + sizeof(guint32));
34     bool retval = m_chunk.get_content(offset, pinyin, phrase_length * sizeof(PinyinKey));
35     if ( !retval )
36         return retval;
37     return m_chunk.get_content(offset + phrase_length * sizeof(PinyinKey), &freq , sizeof(guint32));
38 }
39
40 void PhraseItem::append_pronunciation(PinyinKey * pinyin, guint32 freq){
41     guint8 phrase_length = get_phrase_length();
42     set_n_pronunciation(get_n_pronunciation() + 1);
43     m_chunk.set_content(m_chunk.size(), pinyin, phrase_length * sizeof(PinyinKey));
44     m_chunk.set_content(m_chunk.size(), &freq, sizeof(guint32));
45 }
46
47 void PhraseItem::remove_nth_pronunciation(size_t index){
48     guint8 phrase_length = get_phrase_length();
49     set_n_pronunciation(get_n_pronunciation() - 1);
50     size_t offset = phrase_item_header + phrase_length * sizeof ( utf16_t ) + index * (phrase_length * sizeof (PinyinKey) + sizeof(guint32));
51     m_chunk.remove_content(offset, phrase_length * sizeof(PinyinKey) + sizeof(guint32));
52 }
53
54 bool PhraseItem::get_phrase_string(utf16_t * phrase){
55     guint8 phrase_length = get_phrase_length();
56     return m_chunk.get_content(phrase_item_header, phrase, phrase_length * sizeof(utf16_t));
57 }
58
59 bool PhraseItem::set_phrase_string(guint8 phrase_length, utf16_t * phrase){
60     m_chunk.set_content(0, &phrase_length, sizeof(guint8));
61     m_chunk.set_content(phrase_item_header, phrase, phrase_length * sizeof(utf16_t));
62     return true;
63 }
64
65 void PhraseItem::increase_pinyin_possibility(PinyinCustomSettings & custom,
66                                              PinyinKey * pinyin_keys,
67                                              gint32 delta){
68     guint8 phrase_length = get_phrase_length();
69     guint8 npron = get_n_pronunciation();
70     size_t offset = phrase_item_header + phrase_length * sizeof ( utf16_t );
71     char * buf_begin = (char *) m_chunk.begin();
72     guint32 total_freq = 0;
73     for ( int i = 0 ; i < npron ; ++i){
74         char * pinyin_begin = buf_begin + offset +
75             i * ( phrase_length * sizeof(PinyinKey) + sizeof(guint32) );
76         guint32 * freq = (guint32 *)(pinyin_begin + phrase_length * sizeof(PinyinKey));
77         total_freq += *freq;
78         if ( 0 == pinyin_compare_with_ambiguities(custom,
79                                                   (PinyinKey *)pinyin_begin,
80                                                   pinyin_keys,
81                                                   phrase_length)){
82             //protect against total_freq overflow.
83             if ( delta > 0 && total_freq > total_freq + delta )
84                 return;
85             *freq += delta;
86             total_freq += delta;
87         }
88     }
89 }
90
91
92 guint32 SubPhraseIndex::get_phrase_index_total_freq(){
93     return m_total_freq;
94 }
95
96 int SubPhraseIndex::add_unigram_frequency(phrase_token_t token, guint32 delta){
97     table_offset_t offset;
98     guint32 freq;
99     bool result = m_phrase_index.get_content
100         ((token & PHRASE_MASK) 
101          * sizeof(table_offset_t), &offset, sizeof(table_offset_t));
102
103     if ( !result )
104         return ERROR_OUT_OF_RANGE;
105
106     if ( 0 == offset )
107     return ERROR_NO_ITEM;
108
109     result = m_phrase_content.get_content
110         (offset + sizeof(guint8) + sizeof(guint8), &freq, sizeof(guint32));
111
112     if ( !result )
113     return ERROR_FILE_CORRUPTION;
114
115     //protect total_freq overflow
116     if ( delta > 0 && m_total_freq > m_total_freq + delta )
117         return ERROR_INTEGER_OVERFLOW;
118
119     freq += delta;
120     m_total_freq += delta;
121     m_phrase_content.set_content(offset + sizeof(guint8) + sizeof(guint8), &freq, sizeof(guint32));
122
123     return ERROR_OK;
124 }
125
126 int SubPhraseIndex::get_phrase_item(phrase_token_t token, PhraseItem & item){
127     table_offset_t offset;
128     guint8 phrase_length;
129     guint8 n_prons;
130     
131     bool result = m_phrase_index.get_content
132         ((token & PHRASE_MASK) 
133          * sizeof(table_offset_t), &offset, sizeof(table_offset_t));
134
135     if ( !result )
136         return ERROR_OUT_OF_RANGE;
137
138     if ( 0 == offset )
139     return ERROR_NO_ITEM;
140
141     result = m_phrase_content.get_content(offset, &phrase_length, sizeof(guint8));
142     if ( !result ) 
143     return ERROR_FILE_CORRUPTION;
144     
145     result = m_phrase_content.get_content(offset+sizeof(guint8), &n_prons, sizeof(guint8));
146     if ( !result ) 
147         return ERROR_FILE_CORRUPTION;
148
149     size_t length = phrase_item_header + phrase_length * sizeof ( utf16_t ) + n_prons * ( phrase_length * sizeof (PinyinKey) + sizeof(guint32) );
150     item.m_chunk.set_chunk((char *)m_phrase_content.begin() + offset, length, NULL);
151     return ERROR_OK;
152 }
153
154 int SubPhraseIndex::add_phrase_item(phrase_token_t token, PhraseItem * item){
155     table_offset_t offset = m_phrase_content.size();
156     if ( 0 == offset )
157         offset = 8;
158     m_phrase_content.set_content(offset, item->m_chunk.begin(), item->m_chunk.size());
159     m_phrase_index.set_content((token & PHRASE_MASK) 
160                                * sizeof(table_offset_t), &offset, sizeof(table_offset_t));
161     m_total_freq += item->get_unigram_frequency();
162     return ERROR_OK;
163 }
164
165 int SubPhraseIndex::remove_phrase_item(phrase_token_t token, PhraseItem * & item){
166     PhraseItem old_item;
167
168     int result = get_phrase_item(token, old_item);
169     if (result != ERROR_OK)
170     return result;
171
172     item = new PhraseItem;
173     //implictly copy data from m_chunk_content.
174     item->m_chunk.set_content(0, (char *) old_item.m_chunk.begin() , old_item.m_chunk.size());
175
176     const table_offset_t zero_const = 0;
177     m_phrase_index.set_content((token & PHRASE_MASK)
178                                * sizeof(table_offset_t), &zero_const, sizeof(table_offset_t));
179     m_total_freq -= item->get_unigram_frequency();
180     return ERROR_OK;
181 }
182
183 bool FacadePhraseIndex::load(guint8 phrase_index, MemoryChunk * chunk){
184     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
185     if ( !sub_phrases ){
186         sub_phrases = new SubPhraseIndex;
187     }
188     
189     bool retval = sub_phrases->load(chunk, 0, chunk->size());
190     if ( !retval )
191         return retval;
192     m_total_freq += sub_phrases->get_phrase_index_total_freq();
193     return retval;
194 }
195
196 bool FacadePhraseIndex::store(guint8 phrase_index, MemoryChunk * new_chunk){
197     table_offset_t end;
198     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
199     if ( !sub_phrases )
200         return false;
201     
202     sub_phrases->store(new_chunk, 0, end);
203     return true;
204 }
205
206 bool FacadePhraseIndex::unload(guint8 phrase_index){
207     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
208     if ( !sub_phrases )
209         return false;
210     m_total_freq -= sub_phrases->get_phrase_index_total_freq();
211     delete sub_phrases;
212     sub_phrases = NULL;
213     return true;
214 }
215
216 bool FacadePhraseIndex::diff(guint8 phrase_index, MemoryChunk * oldchunk,
217                              MemoryChunk * newlog){
218     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
219     if ( !sub_phrases )
220         return false;
221
222     SubPhraseIndex old_sub_phrases;
223     old_sub_phrases.load(oldchunk, 0, oldchunk->size());
224     PhraseIndexLogger logger;
225
226     bool retval = sub_phrases->diff(&old_sub_phrases, &logger);
227     logger.store(newlog);
228     return retval;
229 }
230
231 bool FacadePhraseIndex::merge(guint8 phrase_index, MemoryChunk * log){
232     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
233     if ( !sub_phrases )
234         return false;
235
236     PhraseIndexLogger logger;
237     logger.load(log);
238
239     return sub_phrases->merge(&logger);
240 }
241
242 bool SubPhraseIndex::load(MemoryChunk * chunk, 
243                           table_offset_t offset, table_offset_t end){
244     //save the memory chunk
245     if ( m_chunk ){
246         delete m_chunk;
247         m_chunk = NULL;
248     }
249     m_chunk = chunk;
250     
251     char * buf_begin = (char *)chunk->begin();
252     chunk->get_content(offset, &m_total_freq, sizeof(guint32));
253     offset += sizeof(guint32);
254     table_offset_t index_one, index_two, index_three;
255     chunk->get_content(offset, &index_one, sizeof(table_offset_t));
256     offset += sizeof(table_offset_t);
257     chunk->get_content(offset, &index_two, sizeof(table_offset_t));
258     offset += sizeof(table_offset_t);
259     chunk->get_content(offset, &index_three, sizeof(table_offset_t));
260     offset += sizeof(table_offset_t);
261     g_return_val_if_fail(*(buf_begin + offset) == c_separate, FALSE);
262     g_return_val_if_fail(*(buf_begin + index_two - 1) == c_separate, FALSE);
263     g_return_val_if_fail(*(buf_begin + index_three - 1) == c_separate, FALSE);
264     m_phrase_index.set_chunk(buf_begin + index_one, 
265                              index_two - 1 - index_one, NULL);
266     m_phrase_content.set_chunk(buf_begin + index_two, 
267                                  index_three - 1 - index_two, NULL);
268     g_return_val_if_fail( index_three <= end, FALSE);
269     return true;
270 }
271
272 bool SubPhraseIndex::store(MemoryChunk * new_chunk, 
273                            table_offset_t offset, table_offset_t& end){
274     new_chunk->set_content(offset, &m_total_freq, sizeof(guint32));
275     table_offset_t index = offset + sizeof(guint32);
276         
277     offset = index + sizeof(table_offset_t) * 3 ;
278     new_chunk->set_content(offset, &c_separate, sizeof(char));
279     offset += sizeof(char);
280     
281     new_chunk->set_content(index, &offset, sizeof(table_offset_t));
282     index += sizeof(table_offset_t);
283     new_chunk->set_content(offset, m_phrase_index.begin(), m_phrase_index.size());
284     offset += m_phrase_index.size();
285     new_chunk->set_content(offset, &c_separate, sizeof(char));
286     offset += sizeof(char);
287
288     new_chunk->set_content(index, &offset, sizeof(table_offset_t));
289     index += sizeof(table_offset_t);
290     
291     new_chunk->set_content(offset, m_phrase_content.begin(), m_phrase_content.size());
292     offset += m_phrase_content.size();
293     new_chunk->set_content(offset, &c_separate, sizeof(char));
294     offset += sizeof(char);
295     new_chunk->set_content(index, &offset, sizeof(table_offset_t));
296     return true;
297 }
298
299 bool FacadePhraseIndex::load_text(guint8 phrase_index, FILE * infile){
300     SubPhraseIndex * & sub_phrases = m_sub_phrase_indices[phrase_index];
301     if ( !sub_phrases ){
302         sub_phrases = new SubPhraseIndex;
303     }
304
305     char pinyin[256];
306     char phrase[256];
307     phrase_token_t token;
308     size_t freq;
309     PhraseItem * item_ptr = new PhraseItem;
310     phrase_token_t cur_token = 0;
311     while ( !feof(infile)){
312         fscanf(infile, "%s", pinyin);
313         fscanf(infile, "%s", phrase);
314         fscanf(infile, "%u", &token);
315         fscanf(infile, "%ld", &freq);
316         if ( feof(infile) )
317             break;
318
319         assert(PHRASE_INDEX_LIBRARY_INDEX(token) == phrase_index );
320
321         glong written;
322         utf16_t * phrase_utf16 = g_utf8_to_utf16(phrase, -1, NULL, 
323                                                &written, NULL);
324         
325         if ( 0 == cur_token ){
326             cur_token = token;
327             item_ptr->set_phrase_string(written, phrase_utf16);
328         }
329
330         if ( cur_token != token ){
331             add_phrase_item( cur_token, item_ptr);
332             delete item_ptr;
333             item_ptr = new PhraseItem;
334             cur_token = token;
335             item_ptr->set_phrase_string(written, phrase_utf16);
336         }
337
338         PinyinDefaultParser parser;
339         NullPinyinValidator validator;
340         PinyinKeyVector keys;
341         PinyinKeyPosVector poses;
342         
343         keys = g_array_new(FALSE, FALSE, sizeof( PinyinKey));
344         poses = g_array_new(FALSE, FALSE, sizeof( PinyinKeyPos));
345         parser.parse(validator, keys, poses, pinyin);
346         
347         assert ( item_ptr->get_phrase_length() == keys->len );
348         item_ptr->append_pronunciation((PinyinKey *)keys->data, freq);
349
350         g_array_free(keys, TRUE);
351         g_array_free(poses, TRUE);
352         g_free(phrase_utf16);
353     }
354
355     add_phrase_item( cur_token, item_ptr);
356     delete item_ptr;
357     m_total_freq += m_sub_phrase_indices[phrase_index]->get_phrase_index_total_freq();
358     return true;
359 }
360
361 int FacadePhraseIndex::get_range(guint8 phrase_index, /* out */ PhraseIndexRange & range){
362     SubPhraseIndex * sub_phrase = m_sub_phrase_indices[phrase_index];
363     if ( !sub_phrase )
364         return ERROR_NO_SUB_PHRASE_INDEX;
365
366     int result = sub_phrase->get_range(range);
367     if ( result )
368         return result;
369
370     range.m_range_begin = PHRASE_INDEX_MAKE_TOKEN(phrase_index, range.m_range_begin);
371     range.m_range_end = PHRASE_INDEX_MAKE_TOKEN(phrase_index, range.m_range_end);
372     return ERROR_OK;
373 }
374
375 int SubPhraseIndex::get_range(/* out */ PhraseIndexRange & range){
376     const table_offset_t * begin = (const table_offset_t *)m_phrase_index.begin();
377     const table_offset_t * end = (const table_offset_t *)m_phrase_index.end();
378
379     range.m_range_begin = 1; /* token starts with 1 in gen_pinyin_table. */
380     range.m_range_end = end - begin;
381
382     return ERROR_OK;
383 }
384
385 bool FacadePhraseIndex::compat(){
386     for ( size_t index = 0; index < PHRASE_INDEX_LIBRARY_COUNT; ++index) {
387         SubPhraseIndex * sub_phrase = m_sub_phrase_indices[index];
388         if ( !sub_phrase )
389             continue;
390
391         SubPhraseIndex * new_sub_phrase =  new SubPhraseIndex;
392         PhraseIndexRange range;
393         int result = sub_phrase->get_range(range);
394         if ( result != ERROR_OK ) {
395             delete new_sub_phrase;
396             continue;
397         }
398
399         PhraseItem item;
400         for ( phrase_token_t token = range.m_range_begin;
401               token < range.m_range_end;
402               ++token ) {
403             result = sub_phrase->get_phrase_item(token, item);
404             if ( result != ERROR_OK )
405                 continue;
406             new_sub_phrase->add_phrase_item(token, &item);
407         }
408
409         delete sub_phrase;
410         m_sub_phrase_indices[index] = new_sub_phrase;
411     }
412     return true;
413 }