nysol 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "mkmf"
3
+
4
+ unless have_library("kgmod3")
5
+ puts("need libkgmod.")
6
+ puts("refer https://github.com/nysol/mcmd")
7
+ exit 1
8
+ end
9
+
10
+ $LOCAL_LIBS += "-lkgmod3"
11
+ create_makefile("nysol/mtable")
12
+
@@ -0,0 +1,555 @@
1
+ /* ////////// LICENSE INFO ////////////////////
2
+
3
+ * Copyright (C) 2013 by NYSOL CORPORATION
4
+ *
5
+ * Unless you have received this program directly from NYSOL pursuant
6
+ * to the terms of a commercial license agreement with NYSOL, then
7
+ * this program is licensed to you under the terms of the GNU Affero General
8
+ * Public License (AGPL) as published by the Free Software Foundation,
9
+ * either version 3 of the License, or (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful, but
12
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF
13
+ * NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
14
+ *
15
+ * Please refer to the AGPL (http://www.gnu.org/licenses/agpl-3.0.txt)
16
+ * for more details.
17
+
18
+ ////////// LICENSE INFO ////////////////////*/
19
+ // =============================================================================
20
+ // mtable.cpp csv => 行列 クラス
21
+ // =============================================================================
22
+ #include <sstream>
23
+ #include <unistd.h>
24
+ #include <fcntl.h>
25
+ #include <sys/stat.h>
26
+ #include <sys/types.h>
27
+ #include <ruby.h>
28
+ #include <kgmod.h>
29
+ #include <kgMethod.h>
30
+
31
+
32
+ #define BUFSIZE 102400
33
+
34
+ using namespace std;
35
+ using namespace kglib;
36
+
37
+ extern "C" {
38
+ void Init_mtable(void);
39
+ }
40
+
41
+ // -----------------------------------------------------------------------------
42
+ // encodingを考慮した文字列生成
43
+ // -----------------------------------------------------------------------------
44
+ static VALUE str2rbstr(const char *ptr)
45
+ {
46
+ // rb_external_str_new_cstrが定義されているばそちらを使う
47
+ #if defined(rb_external_str_new_cstr)
48
+ return rb_external_str_new_cstr(ptr);
49
+ #else
50
+ return rb_str_new2(ptr);
51
+ #endif
52
+
53
+ }
54
+
55
+ // =============================================================================
56
+ // mtable クラス
57
+ // CSVファイルをメモリに読み込み,行と列の指定で自由にcell情報をセットする
58
+ // ためのkgMod継承クラス.
59
+ // =============================================================================
60
+ class mtable : public kgMod
61
+ {
62
+ kgAutoPtr2<char*> _nameStr_ap;
63
+ kgAutoPtr2<char*> _valStr_ap;
64
+
65
+ char* _buf; // 全データを格納するバッファ
66
+ int _blocks; // read回数。
67
+ vector<char*> _cell; // 全セルへのインデックス
68
+ string _fsName; // i=
69
+ bool _fldNameNo; // -nfn
70
+ char** _nameStr; // 項目名一覧
71
+ size_t _fldSize; // 項目数
72
+ size_t _recSize; // 行数
73
+
74
+ public:
75
+
76
+ mtable(void){ // コンストラクタ
77
+ _name = "mtable";
78
+ _version = "1.0";
79
+ _blocks = 0;
80
+ _fldSize = 0;
81
+ _recSize = 0;
82
+ };
83
+ virtual ~mtable() {}
84
+ //アクセッサ
85
+ void fname(string fn) { _fsName = fn ;}
86
+ void nfn(bool nfnf) { _fldNameNo = nfnf ;}
87
+ bool nfn(void) { return _fldNameNo;}
88
+ char* header(size_t i) { return *(_nameStr+i);}
89
+ size_t fldsize() { return _fldSize;}
90
+ size_t recsize() { return _recSize;}
91
+
92
+ int run();
93
+
94
+ char* cell(size_t col, size_t row){
95
+ return _cell.at(row*_fldSize+col);
96
+ }
97
+ };
98
+
99
+ // -----------------------------------------------------------------------------
100
+ // run
101
+ // -----------------------------------------------------------------------------
102
+ int mtable::run() try
103
+ {
104
+ // ファイルオープン
105
+ int fd = ::open(_fsName.c_str(), O_RDONLY );
106
+ if(fd == -1 ){
107
+ rb_raise(rb_eRuntimeError,"file open error");
108
+ }
109
+
110
+ // 全行読み込む
111
+ size_t totalSize=0;
112
+ _buf=0;
113
+ while(true){
114
+ _blocks++;
115
+ long rbufSz = BUFSIZE*_blocks+1;
116
+ if(rbufSz <long(0)){
117
+ rb_raise(rb_eRuntimeError,"read buffer is full");
118
+ }
119
+ REALLOC_N(_buf, char, rbufSz); // +1は最終行に改行がなかった場合の終端'\0'用
120
+ size_t rsize = ::read(fd, _buf+(BUFSIZE*(_blocks-1)), BUFSIZE);
121
+ if(rsize==0) break;
122
+ totalSize+=rsize;
123
+ }
124
+ ::close(fd);
125
+
126
+ // bufferの終端位置
127
+ char* endPos=_buf+totalSize;
128
+
129
+ if(totalSize==0){
130
+ _fldSize = 0;
131
+ _recSize = 0;
132
+ return 0;
133
+ }
134
+ if(*(endPos-1)!='\n' && *(endPos-1)!='\r'){
135
+ *endPos='\n';
136
+ totalSize++;
137
+ endPos++;
138
+ }
139
+
140
+ // 項目数のカウント
141
+ _fldSize = kglib::cntFldToken(_buf, totalSize);
142
+
143
+ // 項目名(先頭rec)の取得
144
+ char* pnt = _buf;
145
+
146
+ // 1行目が項目名行の場合, 項目名のtoken分割
147
+ if(!nfn()){
148
+ _nameStr_ap.set( new char*[_fldSize] );
149
+ _nameStr = _nameStr_ap.get();
150
+ pnt = sepFldToken(_nameStr, _fldSize , pnt , totalSize)+1;
151
+ for(size_t i=0; i<_fldSize;i++){
152
+ splitToken(_nameStr[i],'%'); // 項目名 % チェック
153
+ }
154
+ }
155
+
156
+ // 項目値char*へのインデックス
157
+ _valStr_ap.set( new char*[_fldSize] );
158
+ char ** valStr = _valStr_ap.get();
159
+
160
+ while(pnt < endPos){
161
+ pnt = sepFldToken(valStr, _fldSize, pnt , endPos-pnt) + 1;
162
+ _recSize++;
163
+ for(size_t i=0; i < _fldSize; i++){
164
+ _cell.push_back( *(valStr+i) );
165
+ }
166
+ }
167
+ return 0;
168
+ }catch(kgError& err){
169
+ ostringstream ss;
170
+ ss << "Error in processing line " << _recSize+1 << "(not including header line)";
171
+ err.addMessage(ss.str());
172
+ throw;
173
+ }catch(...){
174
+ ostringstream ss;
175
+ ss << "Internal error in processing line" << _recSize+1 << "(not including header line)";
176
+ throw kgError(ss.str());
177
+ }
178
+
179
+ // =============================================================================
180
+ // rMmtable クラス(mtableクラスのrubyラッパ)
181
+ // 機能: 1) kgEnvとコマンドライン引数のメモリを確保する
182
+ // 2) 正常終了,エラー終了の統一的メソッドを提供する
183
+ // =============================================================================
184
+ class rMtable
185
+ {
186
+ public:
187
+ kgEnv env;
188
+ string argstr; // rubyの引数をコピーして格納するための変数
189
+ kgAutoPtr2<char*> argv;
190
+ mtable* mod; // 一度実行(RUN)されると0となる.
191
+ VALUE object_; // rMtableオブジェクト(rb_yieldで必要)
192
+
193
+ private:
194
+ void freeMod() try
195
+ {
196
+ if(mod!=0){ delete mod; }
197
+ mod=0;
198
+ }catch(...){
199
+ rb_raise(rb_eRuntimeError,"Error at freeMod()");
200
+ }
201
+
202
+ public:
203
+
204
+ // エラー終了メソッド
205
+ void errorEnd(kgError& err) try
206
+ {
207
+ mod->errorEnd(err);
208
+ freeMod();
209
+ }catch(...){
210
+ rb_raise(rb_eRuntimeError,"Error at errorEnd()");
211
+ }
212
+
213
+ // 正常終了メソッド
214
+ void successEnd() try
215
+ {
216
+ mod->successEnd();
217
+ freeMod();
218
+ }catch(...){
219
+ rb_raise(rb_eRuntimeError,"Error at successEnd()");
220
+ }
221
+ };
222
+
223
+ // =============================================================================
224
+ // 公開メソッド
225
+ // =============================================================================
226
+ //
227
+ // NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
228
+ //
229
+ /*
230
+ * call-seq:
231
+ * Mtable.new -> tbl
232
+ *
233
+ * <b>説明</b> テーブルクラスのインスタンスを生成する.この段階ではファイルはまだ読み込まれない.runメソッドを実行して初めて読み込まれる.
234
+ *
235
+ * <b>書式</b> Mtable.new("i=CSVファイル名 [-nfn]")
236
+ *
237
+ * i=:: 読み込むCSVファイル名を指定する.
238
+ *
239
+ * -nfn=::1 行目を項目名と見なさない
240
+ *
241
+ */
242
+ VALUE mtable_init(int argc, VALUE *argv, VALUE self) try
243
+ {
244
+ rMtable* rmod;
245
+ Data_Get_Struct(self,rMtable,rmod);
246
+
247
+ try
248
+ {
249
+ // 引数セット
250
+ VALUE options;
251
+ rb_scan_args(argc, argv,"01",&options);
252
+
253
+ // rubyの引数文字列を一旦rmod->argstrに退避させてからtoken分割する。
254
+ // 退避させないと、ruby変数としての文字列を変更してしまうことになる。
255
+ if(TYPE(options)==T_NIL){
256
+ rmod->argstr="";
257
+ }else if(TYPE(options)==T_STRING){
258
+ rmod->argstr=RSTRING_PTR(options);
259
+ }else{
260
+ rb_raise(rb_eRuntimeError,"1st argument must be String");
261
+ }
262
+ vector<char *> opts = splitToken(const_cast<char*>(rmod->argstr.c_str()), ' ',true);
263
+
264
+ // 引数文字列へのポインタの領域はここでauto変数に確保する
265
+ char** vv;
266
+ try{
267
+ rmod->argv.set(new char*[opts.size()+1]);
268
+ vv = rmod->argv.get();
269
+ }catch(...){
270
+ rb_raise(rb_eRuntimeError,"memory allocation error");
271
+ }
272
+
273
+ // vv配列0番目はコマンド名
274
+ vv[0]=const_cast<char*>(rmod->mod->name());
275
+
276
+ size_t vvSize;
277
+ for(vvSize=0; vvSize<opts.size(); vvSize++){
278
+ vv[vvSize+1] = opts.at(vvSize);
279
+ }
280
+ vvSize+=1;
281
+
282
+ // modの初期化&引数の存在チェック
283
+ rmod->mod->init(vvSize, const_cast<const char**>(vv), &rmod->env);
284
+ rmod->mod->args()->paramcheck("i=,-nfn",false);
285
+
286
+ // 各種引数の設定 i=,-nfn
287
+ rmod->mod->fname(rmod->mod->args()->toString("i=",false));
288
+ rmod->mod->nfn(rmod->mod->args()->toBool("-nfn"));
289
+
290
+ // メモリにローディング
291
+ rmod->mod->run();
292
+
293
+ // ブロック引数があればyield実行
294
+ if(rb_block_given_p()){
295
+ rb_yield(rmod->object_);
296
+ rmod->successEnd();
297
+ }
298
+
299
+ }catch(kgError& err){
300
+ rmod->errorEnd(err); throw;
301
+ }
302
+ return self;
303
+
304
+ }catch(...){
305
+ rb_raise(rb_eRuntimeError,"Error at mtable_init()");
306
+ }
307
+
308
+ // -----------------------------------------------------------------------------
309
+ // NAMES
310
+ // -----------------------------------------------------------------------------
311
+ /*
312
+ * call-seq:
313
+ * names -> String Array or nil
314
+ *
315
+ * 項目名配列を返す。
316
+ *
317
+ * === 引数
318
+ * なし
319
+ *
320
+ */
321
+ VALUE mtable_names(VALUE self) try
322
+ {
323
+ rMtable* rmod;
324
+ Data_Get_Struct(self,rMtable,rmod);
325
+ VALUE rtn;
326
+ if (rmod->mod->nfn()){
327
+ rtn = Qnil;
328
+ }else{
329
+ rtn = rb_ary_new2(rmod->mod->fldsize());
330
+ for(size_t i=0 ;i<rmod->mod->fldsize(); i++){
331
+ rb_ary_store( rtn, i, str2rbstr( rmod->mod->header(i) ) );
332
+ }
333
+ }
334
+ return rtn;
335
+
336
+ }catch(...){
337
+ rb_raise(rb_eRuntimeError,"Error at mtable_names()");
338
+ }
339
+
340
+ // -----------------------------------------------------------------------------
341
+ // NAME2NUM
342
+ // -----------------------------------------------------------------------------
343
+ /*
344
+ * call-seq:
345
+ * name2num -> String=>Fixnum Hash or nil
346
+ *
347
+ * 項目名をキー、対応する項目番号を値とする Hash を返す。
348
+ *
349
+ * === 引数
350
+ * なし
351
+ *
352
+ */
353
+ VALUE mtable_name2num(VALUE self) try
354
+ {
355
+ rMtable* rmod;
356
+ Data_Get_Struct(self,rMtable,rmod);
357
+ VALUE rtn;
358
+ if (rmod->mod->nfn()){
359
+ rtn = Qnil;
360
+ }else{
361
+ rtn =rb_hash_new();
362
+ for(size_t i=0 ;i<rmod->mod->fldsize(); i++){
363
+ rb_hash_aset(rtn, str2rbstr( rmod->mod->header(i) ) , INT2FIX(i));
364
+ }
365
+ }
366
+ return rtn;
367
+
368
+ }catch(...){
369
+ rb_raise(rb_eRuntimeError,"Error at mtable_names()");
370
+ }
371
+
372
+ // -----------------------------------------------------------------------------
373
+ // SIZE
374
+ // -----------------------------------------------------------------------------
375
+ /*
376
+ * call-seq:
377
+ * size -> Fixnum
378
+ *
379
+ * 行数を返す。
380
+ *
381
+ * === 引数
382
+ * なし
383
+ *
384
+ */
385
+ VALUE mtable_size(VALUE self) try {
386
+ rMtable* rmod;
387
+ Data_Get_Struct(self,rMtable,rmod);
388
+ return INT2FIX(rmod->mod->recsize());
389
+ }catch(...){
390
+ rb_raise(rb_eRuntimeError,"Error at mtable_size()");
391
+ }
392
+
393
+ // -----------------------------------------------------------------------------
394
+ // CELL
395
+ // -----------------------------------------------------------------------------
396
+ /*
397
+ * call-seq:
398
+ * cell -> String
399
+ *
400
+ * row(行),col(列) に対応するセルの値を返す。
401
+ * row,col の与え方は、列番号と行番号による。
402
+ * 列/行番号共に 0 か ら始まる整数 (Mcsvin の列番号は 1 から始まる)。
403
+ * row,col が範囲外の場合は nil を返す。
404
+ * row 行番号で、0 以上の整数を用いる。デフォルトは 0。
405
+ * col 列番号で、0 以上の整数を用いる。項目名は指定できない。デフォルトは 0。
406
+ * col のみ与えると 0 行目の col 番目の項目の値を返す。
407
+ * cell(col,0) を指定したのと同等。
408
+ * また col,row 両方とも与えなければ 0 行目の 0 項目目の値を返す。
409
+ * cell(0,0) を指定したのと同等。
410
+ * === 引数
411
+ * col 列
412
+ * row 行
413
+ */
414
+ VALUE mtable_cell(int argc, VALUE *argv, VALUE self) try {
415
+ rMtable* rmod;
416
+ Data_Get_Struct(self,rMtable,rmod);
417
+
418
+ VALUE option;
419
+ rb_scan_args(argc, argv,"*",&option);
420
+
421
+ size_t colNo=0;
422
+ size_t rowNo=0;
423
+
424
+ if(argc==1){
425
+ // 列番号
426
+ VALUE v1=RARRAY_PTR(option)[0];
427
+ if( TYPE(v1) == T_FIXNUM ){
428
+ colNo = FIX2INT(v1);
429
+ }else if( TYPE(v1) == T_BIGNUM ){
430
+ colNo = NUM2INT(v1);
431
+ }else{
432
+ rb_raise(rb_eRuntimeError,"argument type error (1st argument must be a number");
433
+ }
434
+
435
+ }else if(argc==2){
436
+
437
+ // 列番号
438
+ VALUE v1=RARRAY_PTR(option)[0];
439
+ if( TYPE(v1) == T_FIXNUM ){
440
+ colNo = FIX2INT(v1);
441
+ }else if( TYPE(v1) == T_BIGNUM ){
442
+ colNo = NUM2INT(v1);
443
+ }else{
444
+ rb_raise(rb_eRuntimeError,"argument type error (1st argument must be a number");
445
+ }
446
+
447
+ // 行番号
448
+ VALUE v2=RARRAY_PTR(option)[1];
449
+ if( TYPE(v2) == T_FIXNUM ){
450
+ rowNo = FIX2INT(v2);
451
+ }else if( TYPE(v2) == T_BIGNUM ){
452
+ rowNo = NUM2INT(v2);
453
+ }else{
454
+ rb_raise(rb_eRuntimeError,"argument type error (1st argument must be a number");
455
+ }
456
+ }
457
+
458
+ // 範囲チェック
459
+ if(colNo>=rmod->mod->fldsize()){
460
+ rb_raise(rb_eRuntimeError,"col number must be less than %zd",rmod->mod->fldsize());
461
+ }
462
+ if(rowNo>=rmod->mod->recsize()){
463
+ rb_raise(rb_eRuntimeError,"row number must be less than %zd",rmod->mod->recsize());
464
+ }
465
+
466
+ // 値を返す
467
+ return str2rbstr( rmod->mod->cell(colNo, rowNo) );
468
+
469
+
470
+ }catch(...){
471
+ rb_raise(rb_eRuntimeError,"Error at mtable_records()");
472
+ }
473
+
474
+ // -----------------------------------------------------------------------------
475
+ // メモリ解放
476
+ // kgRubyModの領域開放(GC時にrubyにより実行される)(xxx_alloc()にて登録される)
477
+ // -----------------------------------------------------------------------------
478
+ void mtable_free(rMtable* rmod) try
479
+ {
480
+ if(rmod!=0){ delete rmod; }
481
+
482
+ }catch(...){
483
+ rb_raise(rb_eRuntimeError,"Error at mtable_free()");
484
+ }
485
+
486
+ // -----------------------------------------------------------------------------
487
+ // インスタンス化される時のメモリ確保
488
+ // -----------------------------------------------------------------------------
489
+ VALUE mtable_alloc(VALUE klass) try
490
+ {
491
+ rMtable* rmod=new rMtable;
492
+ rmod->mod = new mtable;
493
+ VALUE object=Data_Wrap_Struct(klass,0,mtable_free,rmod);
494
+ rmod->object_ = object;
495
+ return object;
496
+
497
+ }catch(...){
498
+ rb_raise(rb_eRuntimeError,"Error at table_alloc()");
499
+ }
500
+
501
+ // -----------------------------------------------------------------------------
502
+ // ruby Mtable クラス init
503
+ // -----------------------------------------------------------------------------
504
+ /*
505
+ * = 指定した CSV データ全体をメモリに読み込んで処理するクラス以下のような特徴を持つ。
506
+ * 行と列の指定によりセルをランダムにアクセス可能。
507
+ * 読み込み専用であり、データの更新や追加は一切できない。
508
+ * データは全て文字列として読み込むので、その他の方で利用する時は適宜型変換 (ex. to i) が必要。
509
+ * メモリが空いている限りデータを読み込む。領域がなくなればエラー終了する。
510
+ * === 利用例1
511
+ * # dat1.csv
512
+ * customer,date,amount
513
+ * A,20081201,10
514
+ * B,20081002,40
515
+ *
516
+ * tbl=Mtable.new("i=dat1.csv")
517
+ * p tbl.names # -> ["customer", "date", "amount"]
518
+ * p tbl.name2num # -> {"amount"=>2, "date"=>1, "customer"=>0}
519
+ * p tbl.size # -> 2
520
+ * p tbl.cell(0,0) # -> "A"
521
+ * p tbl.cell(0,1) # -> "B"
522
+ * p tbl.cell(1,1) # -> "20081202"
523
+ * p tbl.cell(1) # -> "20081201"
524
+ * p tbl.cell # -> "A"
525
+ * === 利用例2
526
+ * # dat1.csv
527
+ * customer,date,amount
528
+ * A,20081201,10
529
+ * B,20081002,40
530
+ *
531
+ * tbl=Mtable.new("i=dat1.csv -nfn") # 一行目もデータと見なす。
532
+ * p tbl.names # -> nil
533
+ * p tbl.name2num # -> nil
534
+ * p tbl.size # -> 3
535
+ * p tbl.cell(0,0) # -> "customer"
536
+ * p tbl.cell(0,1) # -> "A"
537
+ * p tbl.cell(1,1) # -> "20081201"
538
+ * p tbl.cell(1) # -> "date"
539
+ * p tbl.cell # -> "customer"
540
+ */
541
+ void Init_mtable(void)
542
+ {
543
+ // モジュール定義:MCMD::xxxxの部分
544
+ VALUE mcmd=rb_define_module("MCMD");
545
+
546
+ VALUE mtable;
547
+ mtable=rb_define_class_under(mcmd,"Mtable",rb_cObject);
548
+ rb_define_alloc_func(mtable, mtable_alloc);
549
+ rb_define_method(mtable,"initialize", (VALUE (*)(...))mtable_init , -1);
550
+ rb_define_method(mtable,"names" , (VALUE (*)(...))mtable_names , 0);
551
+ rb_define_method(mtable,"name2num" , (VALUE (*)(...))mtable_name2num, 0);
552
+ rb_define_method(mtable,"cell" , (VALUE (*)(...))mtable_cell , -1);
553
+ rb_define_method(mtable,"size" , (VALUE (*)(...))mtable_size , 0);
554
+ }
555
+