ferret 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/README +6 -5
  2. data/Rakefile +34 -13
  3. data/TODO +1 -0
  4. data/TUTORIAL +1 -1
  5. data/ext/analysis.c +87 -70
  6. data/ext/analysis.h +18 -6
  7. data/ext/array.c +1 -2
  8. data/ext/array.h +1 -1
  9. data/ext/bitvector.c +10 -6
  10. data/ext/bitvector.h +2 -2
  11. data/ext/compound_io.c +30 -27
  12. data/ext/document.c +15 -15
  13. data/ext/document.h +5 -5
  14. data/ext/except.c +2 -0
  15. data/ext/except.h +25 -23
  16. data/ext/extconf.rb +1 -0
  17. data/ext/ferret.c +10 -8
  18. data/ext/ferret.h +9 -8
  19. data/ext/field.c +29 -25
  20. data/ext/filter.c +52 -14
  21. data/ext/frtio.h +13 -0
  22. data/ext/fs_store.c +115 -170
  23. data/ext/global.c +9 -8
  24. data/ext/global.h +17 -13
  25. data/ext/hash.c +13 -19
  26. data/ext/hash.h +11 -11
  27. data/ext/hashset.c +5 -7
  28. data/ext/hashset.h +9 -8
  29. data/ext/helper.c +1 -1
  30. data/ext/helper.h +2 -1
  31. data/ext/inc/except.h +25 -23
  32. data/ext/inc/lang.h +11 -1
  33. data/ext/ind.c +33 -21
  34. data/ext/index.h +44 -39
  35. data/ext/index_io.c +61 -57
  36. data/ext/index_rw.c +418 -361
  37. data/ext/lang.c +10 -0
  38. data/ext/lang.h +11 -1
  39. data/ext/nix_io.c +135 -0
  40. data/ext/priorityqueue.c +16 -16
  41. data/ext/priorityqueue.h +9 -6
  42. data/ext/q_boolean.c +128 -76
  43. data/ext/q_const_score.c +20 -20
  44. data/ext/q_filtered_query.c +20 -20
  45. data/ext/q_fuzzy.c +37 -23
  46. data/ext/q_match_all.c +15 -19
  47. data/ext/q_multi_phrase.c +87 -46
  48. data/ext/q_parser.c +247 -119
  49. data/ext/q_phrase.c +86 -52
  50. data/ext/q_prefix.c +25 -14
  51. data/ext/q_range.c +59 -14
  52. data/ext/q_span.c +263 -172
  53. data/ext/q_term.c +62 -51
  54. data/ext/q_wildcard.c +24 -13
  55. data/ext/r_analysis.c +328 -80
  56. data/ext/r_doc.c +11 -6
  57. data/ext/r_index_io.c +40 -32
  58. data/ext/r_qparser.c +15 -14
  59. data/ext/r_search.c +270 -152
  60. data/ext/r_store.c +32 -17
  61. data/ext/ram_store.c +38 -22
  62. data/ext/search.c +617 -87
  63. data/ext/search.h +227 -163
  64. data/ext/similarity.c +54 -45
  65. data/ext/similarity.h +3 -3
  66. data/ext/sort.c +132 -53
  67. data/ext/store.c +21 -2
  68. data/ext/store.h +14 -14
  69. data/ext/tags +4322 -232
  70. data/ext/term.c +140 -109
  71. data/ext/termdocs.c +74 -60
  72. data/ext/vector.c +181 -152
  73. data/ext/w32_io.c +150 -0
  74. data/lib/ferret.rb +1 -1
  75. data/lib/ferret/analysis/standard_tokenizer.rb +4 -3
  76. data/lib/ferret/document/field.rb +1 -1
  77. data/lib/ferret/index/field_infos.rb +1 -1
  78. data/lib/ferret/index/term.rb +1 -1
  79. data/lib/ferret/query_parser/query_parser.tab.rb +8 -24
  80. data/lib/ferret/search.rb +1 -0
  81. data/lib/ferret/search/boolean_query.rb +0 -4
  82. data/lib/ferret/search/index_searcher.rb +21 -8
  83. data/lib/ferret/search/multi_phrase_query.rb +7 -0
  84. data/lib/ferret/search/multi_searcher.rb +261 -0
  85. data/lib/ferret/search/phrase_query.rb +1 -1
  86. data/lib/ferret/search/query.rb +34 -5
  87. data/lib/ferret/search/sort.rb +7 -3
  88. data/lib/ferret/search/sort_field.rb +8 -4
  89. data/lib/ferret/store/fs_store.rb +13 -6
  90. data/lib/ferret/store/index_io.rb +0 -14
  91. data/lib/ferret/store/ram_store.rb +3 -2
  92. data/lib/rferret.rb +1 -1
  93. data/test/unit/analysis/ctc_analyzer.rb +131 -0
  94. data/test/unit/analysis/ctc_tokenstream.rb +98 -9
  95. data/test/unit/index/tc_index.rb +40 -1
  96. data/test/unit/index/tc_term.rb +7 -0
  97. data/test/unit/index/th_doc.rb +8 -0
  98. data/test/unit/query_parser/tc_query_parser.rb +6 -4
  99. data/test/unit/search/rtc_sort_field.rb +6 -6
  100. data/test/unit/search/tc_index_searcher.rb +8 -0
  101. data/test/unit/search/tc_multi_searcher.rb +275 -0
  102. data/test/unit/search/tc_multi_searcher2.rb +126 -0
  103. data/test/unit/search/tc_search_and_sort.rb +66 -0
  104. metadata +31 -26
  105. data/test/unit/query_parser/rtc_query_parser.rb +0 -138
data/ext/frtio.h ADDED
@@ -0,0 +1,13 @@
1
+ #ifndef FRT_IO_H
2
+ #define FRT_IO_H
3
+
4
+ extern char *join_path(char *buf, const char *base, const char *filename);
5
+ extern bool exists(char *path);
6
+ extern int fcount(char *path);
7
+ extern void dir_each(char *path, void (*func)(char *fname, void *arg), void *arg);
8
+ extern void fs_clear_locks(Store *store);
9
+ extern void fs_clear(Store *store);
10
+ extern void fs_clear_all(Store *store);
11
+
12
+ #endif
13
+
data/ext/fs_store.c CHANGED
@@ -1,28 +1,36 @@
1
+ #include "store.h"
2
+ #include "frtio.h"
3
+ #ifdef WIN32
4
+ # ifndef S_IRUSR
5
+ # define S_IRUSR _S_IREAD
6
+ # endif
7
+ # ifndef S_IWUSR
8
+ # define S_IWUSR _S_IWRITE
9
+ # endif
10
+ # include <io.h>
11
+ #else
12
+ #include <unistd.h>
13
+ # define _close close
14
+ # define _open open
15
+ # define _creat creat
16
+ # define _lseek lseek
17
+ # define _read read
18
+ # define _stat stat
19
+ # define _fstat fstat
20
+ #endif
1
21
  #include <sys/types.h>
2
- #include <sys/dir.h>
3
- #include <sys/stat.h>
4
- #include <dirent.h>
5
22
  #include <fcntl.h>
6
- #include <unistd.h>
7
-
23
+ #include <sys/stat.h>
8
24
  #include <errno.h>
9
25
  #include <string.h>
10
26
  #include <stdio.h>
11
- #include "store.h"
27
+
12
28
 
13
29
  static char * const FILE_OPEN_ERROR_MSG = "Couldn't open the file to read";
14
30
  static char * const SEEK_ERROR_MSG = "Seek error message";
15
31
  static char * const WRITE_ERROR_MSG = "Write error message";
16
32
 
17
- /**
18
- * Create a filepath for a file in the store using the operating systems
19
- * default file seperator.
20
- */
21
- static char *join_path(char *buf, const char *base, const char *filename)
22
- {
23
- sprintf(buf, "%s/%s", base, filename);
24
- return buf;
25
- }
33
+ extern void store_destroy(Store *store);
26
34
 
27
35
  /**
28
36
  * Create the file +filename+ in the +store+.
@@ -31,13 +39,15 @@ static char *join_path(char *buf, const char *base, const char *filename)
31
39
  * @param filename the name of the file to create
32
40
  * @throws IO_ERROR if the file cannot be created
33
41
  */
34
- void fs_touch(Store *store, char *filename)
42
+ static void fs_touch(Store *store, char *filename)
35
43
  {
36
- char buf[MAX_PATH];
37
- int fd = creat(join_path(buf, store->dir.path, filename), S_IRUSR|S_IWUSR);
38
- if (fd == -1)
44
+ int f;
45
+ char path[MAX_FILE_PATH];
46
+ join_path(path, store->dir.path, filename);
47
+ if ((f = _creat(path, S_IRUSR|S_IWUSR)) == 0) {
39
48
  RAISE(IO_ERROR, strerror(errno));
40
- close(fd);
49
+ }
50
+ _close(f);
41
51
  }
42
52
 
43
53
  /**
@@ -50,15 +60,8 @@ void fs_touch(Store *store, char *filename)
50
60
  */
51
61
  int fs_exists(Store *store, char *filename)
52
62
  {
53
- char buf[MAX_PATH];
54
- int fd = open(join_path(buf, store->dir.path, filename), 0);
55
- if (fd < 0) {
56
- if (errno != ENOENT)
57
- RAISE(IO_ERROR, strerror(errno));
58
- return false;
59
- }
60
- close(fd);
61
- return true;
63
+ char buf[MAX_FILE_PATH];
64
+ return exists(join_path(buf, store->dir.path, filename));
62
65
  }
63
66
 
64
67
  /**
@@ -71,7 +74,7 @@ int fs_exists(Store *store, char *filename)
71
74
  */
72
75
  int fs_remove(Store *store, char *filename)
73
76
  {
74
- char buf[MAX_PATH];
77
+ char buf[MAX_FILE_PATH];
75
78
  return remove(join_path(buf, store->dir.path, filename));
76
79
  }
77
80
 
@@ -86,10 +89,17 @@ int fs_remove(Store *store, char *filename)
86
89
  */
87
90
  int fs_rename(Store *store, char *from, char *to)
88
91
  {
89
- char buf1[MAX_PATH], buf2[MAX_PATH];
92
+ char buf1[MAX_FILE_PATH], buf2[MAX_FILE_PATH];
93
+
94
+ #ifdef WIN32
95
+ remove(join_path(buf1, store->dir.path, to));
96
+ #endif
90
97
 
91
- return rename(join_path(buf1, store->dir.path, from),
92
- join_path(buf2, store->dir.path, to));
98
+ if (rename(join_path(buf1, store->dir.path, from),
99
+ join_path(buf2, store->dir.path, to)) < 0) {
100
+ RAISE(IO_ERROR, strerror(errno));
101
+ }
102
+ return true;
93
103
  }
94
104
 
95
105
  /**
@@ -101,20 +111,7 @@ int fs_rename(Store *store, char *from, char *to)
101
111
  */
102
112
  int fs_count(Store *store)
103
113
  {
104
- int cnt = 0;
105
- struct dirent *de;
106
- DIR *d = opendir(store->dir.path);
107
-
108
- if (!d) RAISE(IO_ERROR, strerror(errno));
109
-
110
- while ((de = readdir(d)) != NULL) {
111
- if (de->d_name[0] != '.') {
112
- cnt++;
113
- }
114
- }
115
- closedir(d);
116
-
117
- return cnt;
114
+ return fcount(store->dir.path);
118
115
  }
119
116
 
120
117
  /**
@@ -130,39 +127,7 @@ int fs_count(Store *store)
130
127
  */
131
128
  void fs_each(Store *store, void (*func)(char *fname, void *arg), void *arg)
132
129
  {
133
- struct dirent *de;
134
- DIR *d = opendir(store->dir.path);
135
-
136
- if (!d) RAISE(IO_ERROR, strerror(errno));
137
-
138
- while ((de = readdir(d)) != NULL) {
139
- if ((strncmp(de->d_name, LOCK_PREFIX, strlen(LOCK_PREFIX)) == 0) ||
140
- (de->d_name[0] == '.'))
141
- continue;
142
- func(de->d_name, arg);
143
- }
144
- closedir(d);
145
- }
146
-
147
- /**
148
- * Clear all the locks in the store.
149
- *
150
- * @param store the store to clear the locks from
151
- * @throws IO_ERROR if there is an error opening the directory
152
- */
153
- void fs_clear_locks(Store *store)
154
- {
155
- struct dirent *de;
156
- DIR *d = opendir(store->dir.path);
157
-
158
- if (!d) RAISE(IO_ERROR, strerror(errno));
159
-
160
- while ((de = readdir(d)) != NULL) {
161
- if (file_is_lock(de->d_name)) {
162
- fs_remove(store, de->d_name);
163
- }
164
- }
165
- closedir(d);
130
+ dir_each(store->dir.path, func, arg);
166
131
  }
167
132
 
168
133
  /**
@@ -171,54 +136,13 @@ void fs_clear_locks(Store *store)
171
136
  * @param p the store to destroy
172
137
  * @throws IO_ERROR if there is an error deleting the locks
173
138
  */
174
- void fs_destroy(void *p)
139
+ void fs_destroy(Store *store)
175
140
  {
176
- Store *store = (Store *)p;
177
141
  fs_clear_locks(store);
178
142
  free(store->dir.path);
179
143
  store_destroy(store);
180
144
  }
181
145
 
182
- /**
183
- * Clear all files from the store except the lock files.
184
- *
185
- * @param store the store to clear all the files from
186
- * @throws IO_ERROR if there is an error deleting the files
187
- */
188
- void fs_clear(Store *store)
189
- {
190
- struct dirent *de;
191
- DIR *d = opendir(store->dir.path);
192
-
193
- if (!d) RAISE(IO_ERROR, strerror(errno));
194
-
195
- while ((de = readdir(d)) != NULL) {
196
- if (de->d_name[0] != '.' && !file_is_lock(de->d_name)) {
197
- fs_remove(store, de->d_name);
198
- }
199
- }
200
- closedir(d);
201
- }
202
-
203
- /**
204
- * Clear all files from the store including the lock files.
205
- *
206
- * @param store the store to clear all the files from
207
- * @throws IO_ERROR if there is an error deleting the files
208
- */
209
- void fs_clear_all(Store *store)
210
- {
211
- struct dirent *de;
212
- DIR *d = opendir(store->dir.path);
213
-
214
- while ((de = readdir(d)) != NULL) {
215
- if (de->d_name[0] != '.') {
216
- fs_remove(store, de->d_name);
217
- }
218
- }
219
- closedir(d);
220
- }
221
-
222
146
  /**
223
147
  * Return the length of the file +filename+ in +store+
224
148
  *
@@ -229,7 +153,7 @@ void fs_clear_all(Store *store)
229
153
  */
230
154
  int fs_length(Store *store, char *filename)
231
155
  {
232
- char buf[MAX_PATH];
156
+ char buf[MAX_FILE_PATH];
233
157
  struct stat stt;
234
158
 
235
159
  if (stat(join_path(buf, store->dir.path, filename), &stt))
@@ -288,11 +212,12 @@ void fso_close_internal(OutStream *os)
288
212
  */
289
213
  OutStream *fs_create_output(Store *store, const char *filename)
290
214
  {
291
- char buf[MAX_PATH];
215
+ char buf[MAX_FILE_PATH];
292
216
  FILE *f = fopen(join_path(buf, store->dir.path, filename), "wb");
217
+ OutStream *os;
293
218
  if (!f) RAISE(IO_ERROR, strerror(errno));
294
219
 
295
- OutStream *os = os_create();
220
+ os = os_create();
296
221
  os->file = f;
297
222
  os->flush_internal = &fso_flush_internal;
298
223
  os->seek_internal = &fso_seek_internal;
@@ -315,11 +240,14 @@ void fsi_read_internal(InStream *is, uchar *b, int offset, int len)
315
240
  {
316
241
  int fd = (int)is->file;
317
242
  int pos = is_pos(is);
318
- if (pos != lseek(fd, 0, SEEK_CUR)) {
319
- lseek(fd, pos, SEEK_SET);
243
+ if (pos != _lseek(fd, 0, SEEK_CUR)) {
244
+ _lseek(fd, pos, SEEK_SET);
320
245
  }
321
- if (read(fd, b, len) != len) {
322
- RAISE(EOF_ERROR, strerror(errno));
246
+ if (_read(fd, b, len) != len) {
247
+ /* win: the wrong value can be returned for some reason so double check */
248
+ if (_lseek(fd, 0, SEEK_CUR) != (pos + len)) {
249
+ RAISE(EOF_ERROR, strerror(errno));
250
+ }
323
251
  }
324
252
  }
325
253
 
@@ -332,8 +260,9 @@ void fsi_read_internal(InStream *is, uchar *b, int offset, int len)
332
260
  */
333
261
  void fsi_seek_internal(InStream *is, int pos)
334
262
  {
335
- if (lseek((int)is->file, pos, SEEK_SET) < 0)
263
+ if (_lseek((int)is->file, pos, SEEK_SET) < 0) {
336
264
  RAISE(IO_ERROR, strerror(errno));
265
+ }
337
266
  }
338
267
 
339
268
  /**
@@ -345,18 +274,13 @@ void fsi_seek_internal(InStream *is, int pos)
345
274
  void fsi_close_internal(InStream *is)
346
275
  {
347
276
  if (!is->is_clone) {
348
- if (close((int)is->file))
277
+ if (_close((int)is->file)) {
349
278
  RAISE(IO_ERROR, strerror(errno));
279
+ }
350
280
  free(is->d.path);
351
281
  }
352
282
  }
353
283
 
354
- /**
355
- * Clone the input stream. Nothing to do for a file system input stream
356
- */
357
- void fsi_clone_internal(InStream *is, InStream *new_is)
358
- { }
359
-
360
284
  /**
361
285
  * Returns the length of the input stream +is+
362
286
  *
@@ -365,12 +289,19 @@ void fsi_clone_internal(InStream *is, InStream *new_is)
365
289
  */
366
290
  int fsi_length(InStream *is)
367
291
  {
368
- struct stat stt;
369
- if (fstat((int)is->file, &stt))
292
+ struct _stat stt;
293
+ if (_fstat((int)is->file, &stt)) {
370
294
  RAISE(IO_ERROR, strerror(errno));
295
+ }
371
296
  return stt.st_size;
372
297
  }
373
298
 
299
+ /**
300
+ * Clone the input stream. Nothing to do for a file system input stream
301
+ */
302
+ void fsi_clone_internal(InStream *is, InStream *new_is)
303
+ { }
304
+
374
305
  /**
375
306
  * Open an input stream in the +store+ with the name +filename+
376
307
  *
@@ -380,11 +311,14 @@ int fsi_length(InStream *is)
380
311
  */
381
312
  InStream *fs_open_input(Store *store, const char *filename)
382
313
  {
383
- char buf[MAX_PATH];
384
- int fd = open(join_path(buf, store->dir.path, filename), O_RDONLY);
385
- if (fd < 0) RAISE(IO_ERROR, FILE_OPEN_ERROR_MSG);
386
-
387
- InStream *is = is_create();
314
+ InStream *is;
315
+ char buf[MAX_FILE_PATH];
316
+ int fd = _open(join_path(buf, store->dir.path, filename), O_RDONLY);
317
+ if (fd < 0) {
318
+ RAISE(IO_ERROR, FILE_OPEN_ERROR_MSG);
319
+ }
320
+
321
+ is = is_create();
388
322
  is->file = (void *)fd;
389
323
  is->d.path = estrdup(buf);
390
324
  is->is_clone = false;
@@ -407,11 +341,12 @@ int fs_lock_obtain(Lock *lock)
407
341
  {
408
342
  int f;
409
343
  int trys = LOCK_OBTAIN_TIMEOUT;
410
- while (((f = open(lock->name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) &&
411
- (trys > 0))
344
+ while (((f = _open(lock->name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) &&
345
+ (trys > 0)) {
412
346
  trys--;
347
+ }
413
348
  if (f >= 0) {
414
- close(f);
349
+ _close(f);
415
350
  return true;
416
351
  } else {
417
352
  return false;
@@ -426,10 +361,11 @@ int fs_lock_obtain(Lock *lock)
426
361
  */
427
362
  int fs_lock_is_locked(Lock *lock)
428
363
  {
429
- int f = open(lock->name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
364
+ int f = _open(lock->name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
430
365
  if (f >= 0) {
431
- if (close(f) || remove(lock->name))
366
+ if (_close(f) || remove(lock->name)) {
432
367
  RAISE(IO_ERROR, strerror(errno));
368
+ }
433
369
  return false;
434
370
  } else {
435
371
  return true;
@@ -445,7 +381,7 @@ Lock *fs_open_lock(Store *store, char *lockname)
445
381
  {
446
382
  Lock *lock = ALLOC(Lock);
447
383
  char lname[100];
448
- char buf[MAX_PATH];
384
+ char buf[MAX_FILE_PATH];
449
385
  sprintf(lname, "%s%s.lck", LOCK_PREFIX, lockname);
450
386
  lock->name = estrdup(join_path(buf, store->dir.path, lname));
451
387
  lock->obtain = &fs_lock_obtain;
@@ -461,6 +397,20 @@ void fs_close_lock(Lock *lock)
461
397
  free(lock);
462
398
  }
463
399
 
400
+ #ifdef WIN32
401
+ static HshTable stores = {
402
+ /* fill */0,
403
+ /* used */0,
404
+ /* mask */Hsh_MINSIZE - 1,
405
+ /* table */stores.smalltable,
406
+ /* smalltable */{0},
407
+ /* lookup */&h_lookup_str,
408
+ /* hash */NULL,
409
+ /* eq */NULL,
410
+ /* free_key */(free_ft)&dummy_free,
411
+ /* free_value */(free_ft)&fs_destroy
412
+ };
413
+ #else
464
414
  static HshTable stores = {
465
415
  fill:0,
466
416
  used:0,
@@ -469,38 +419,33 @@ static HshTable stores = {
469
419
  lookup:&h_lookup_str,
470
420
  hash:NULL,
471
421
  eq:NULL,
472
- free_key:dummy_free,
473
- free_value:&fs_destroy
422
+ free_key:(free_ft)&dummy_free,
423
+ free_value:(free_ft)&fs_destroy
474
424
  };
425
+ #endif
475
426
 
476
427
  #ifndef FERRET_EXT
477
428
  static mutex_t stores_mutex = MUTEX_INITIALIZER;
478
429
  #endif
479
430
 
480
- void fs_close(Store *store)
431
+ void fs_close_i(Store *store)
481
432
  {
482
- mutex_lock(&store->mutex);
483
- if (--(store->ref_cnt) == 0) {
484
- mutex_lock(&stores_mutex);
485
- h_del(&stores, store->dir.path);
486
- mutex_unlock(&stores_mutex);
487
- } else {
488
- mutex_unlock(&store->mutex);
489
- }
433
+ mutex_lock(&stores_mutex);
434
+ h_del(&stores, store->dir.path);
435
+ mutex_unlock(&stores_mutex);
490
436
  }
491
437
 
492
438
  Store *fs_store_create(const char *pathname)
493
439
  {
494
440
  Store *new_store = store_create();
495
-
496
- new_store->ref_cnt = 0;
441
+
497
442
  new_store->dir.path = estrdup(pathname);
498
443
  new_store->touch = &fs_touch;
499
444
  new_store->exists = &fs_exists;
500
445
  new_store->remove = &fs_remove;
501
446
  new_store->rename = &fs_rename;
502
447
  new_store->count = &fs_count;
503
- new_store->close = &fs_close;
448
+ new_store->close_i = &fs_close_i;
504
449
  new_store->clear = &fs_clear;
505
450
  new_store->clear_all = &fs_clear_all;
506
451
  new_store->clear_locks = &fs_clear_locks;
@@ -519,15 +464,15 @@ Store *open_fs_store(const char *pathname)
519
464
 
520
465
  mutex_lock(&stores_mutex);
521
466
  store = h_get(&stores, pathname);
522
- if (!store) {
467
+ if (store) {
468
+ mutex_lock(&store->mutex);
469
+ store->ref_cnt++;
470
+ mutex_unlock(&store->mutex);
471
+ } else {
523
472
  store = fs_store_create(pathname);
524
- h_set(&stores, pathname, store);
473
+ h_set(&stores, store->dir.path, store);
525
474
  }
526
475
  mutex_unlock(&stores_mutex);
527
476
 
528
- mutex_lock(&store->mutex);
529
- store->ref_cnt++;
530
- mutex_unlock(&store->mutex);
531
-
532
477
  return store;
533
478
  }