summaryrefslogtreecommitdiff
path: root/src/hdb
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2011-10-08 20:30:28 +0200
committerHenrik Rydberg <rydberg@euromail.se>2011-10-08 20:30:28 +0200
commit5df79c53745fde5d6c3340a2979b1429cd5892c1 (patch)
tree1a81af141708b826e9c61e8a04019994fcca8298 /src/hdb
Initial import of htcd system 1.0
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Diffstat (limited to 'src/hdb')
-rw-r--r--src/hdb/README154
-rw-r--r--src/hdb/buffer.h38
-rw-r--r--src/hdb/comp.cc78
-rw-r--r--src/hdb/cvt.cc83
-rw-r--r--src/hdb/db.cc276
-rw-r--r--src/hdb/db.h129
-rw-r--r--src/hdb/filemap.cc302
-rw-r--r--src/hdb/filemap.h196
-rw-r--r--src/hdb/hdbcvt.cc50
-rw-r--r--src/hdb/hdbfix.cc140
-rw-r--r--src/hdb/hdbtest.cc153
-rw-r--r--src/hdb/sortable.cc81
-rw-r--r--src/hdb/sortable.h138
-rw-r--r--src/hdb/test.cc52
14 files changed, 1870 insertions, 0 deletions
diff --git a/src/hdb/README b/src/hdb/README
new file mode 100644
index 0000000..0db7269
--- /dev/null
+++ b/src/hdb/README
@@ -0,0 +1,154 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20HDB/1.2 FILE FORMAT SPECIFICATION Sat Feb 24 18:38:25 MET 2001
21
22MT AND MP SAFE
23
24One single format utilizing memory maps, and which is designed to
25work safely between processes and within a multi-threading process.
26To accomplish this, there are two types of locking mechanisms -
27one inter-process lock in the file header, and one MT lock implemented
28the usual way.
29
30The format is robust within each thread, except for changes in the
31table structure, which generates fatal error_t errors.
32
33The format includes a finite (32) number of tables, which should not
34be a real problem. Besides, a possible change is to move the frame
35pointer to a position inside, so that additional header could be
36added if needed.
37
38FILEFORMAT in HDB/1.2
39
40BYTES 1.2 NAME DESCRIPTION
4132 IDENT Identification header
424 LOCK Inter-process lock
434 MINOR Minor modification state number
444 MAJOR Major modification state number
454 FATAL Fatal modification state number
464 FILESIZE Size of file
474 FILETOP Size of used part
484 TABLES Number of table name strings
494 INDEX Which column is the order column.
5032 COMPNAME Comparison name
5132*32 TABLENAMES Table names
524 FRAME Start of record data
53* RECORDS Record list
54 4 mem amount of memory for this record
55 4 caps the capacity of this record - 0 means deleted
56 mem-8 data The data follows
570 ZEROS The rest of the file is padded with zeros.
58
59The records are always staying where they land; a minor change means
60a change in-place, a major change means a change leading to an index
61update, which could be a deletion, an insertion. Those are updated at
62will, and not seen until wanted. A fatal change is a change in the
63table structure, i.e., an insertion or deletion of a table. Also a
64table name change counts as fatal. The filesize used to be (earlier
65versions) a state needing attention as well, but not anymore.
66
67--
68
69HDB/1.11 FILE FORMAT SPECIFICATION Mon Jan 29 03:51:41 MET 2001
70
71STATIC/DYNAMIC mapping
72
73The header field determines whether the mapping is dynamic or static,
74i.e., static mapping creates a shared mmap and edits the records
75directly, whereas the dynamic mapping creates a private map and
76stores the changes on save only.
77
78The format changed quite a lot after all...
79
80FILEFORMAT in HDB/1.11
81
82BYTES 1.11 NAME DESCRIPTION
8332 IDENT Identification header
844 MODNUM Modification number
854 LOCK Soft lock
864 FILESIZE Size of file
874 FILETOP Size of used part
884 FIXED Whether fixed mapping or not
894 TABLES Number of table name strings
904 INDEX Which column is the order column.
9132 COMPNAME Comparison name
924 RECORDS Number of records in file.
93 tables
94 4,* name table name
95 4,* defaults default string
96 4 caps default sizes
97 [4],* records TABLES[RECORDS]
98
99Size info [4] always refers to the stringref-size, but the number of
100bytes actually written is as follows: for fixed maps, it is tabs[].caps,
101and for dynamic maps, it is capsize(), which is defined as (n=stringref-size)
102(4+n)&~3.
103
104--
105
106HDB/1.10 FILE FORMAT SPECIFICATION Thu Jan 25 00:42:49 MET 2001
107
108The HDB/1.0 format has been used widely in the software
109for our group (MST) during the last three years of so.
110The HDB/1.1 file format is binary compatible with 1.0,
111and merely consists of a formalization, along with a
112change in the internal manipulation scheme and a new
113hdb editor.
114
115FILE FORMAT IN 1.0 and 1.1
116
117BYTES 1.0/1.1 NAME DESCRIPTION
11832/variable HEAD Identification header.
119 Used to be exactly 32 bytes,
120 but see no point in that.
1214 TABLES Number of table name strings
122 4 dim bytes in string
123 * data string data
1244 DEFAULTS Number of default strings
125 4 dim bytes in string
126 * data string data
1274 ORDERBY Which column is the order column.
1284 dim Number of bytes in compname.
129* data string data
1304 RECORDS Number of records in file.
131 4 tables
132 4 dim
133 * data
1340/variable FOOTER File ends. Simple Footer.
135
136ADDITIONAL CHANGES BETWEEN 1.0 and 1.1
137
138The major difference - and the rationale for 1.1 - is
139the possiblity to edit files directly, as in mmap.
140To be able to do this, there is a need to force a minimum
141size to each field. This is done by padding the data
142with trailing zeros. This is ok since only string data
143is stored in the fields, and never binary data. Just think
144of it; if it was binary data, it would not be replaced
145by something the same size anyways, so thats forbidden
146in the direct-editing mode. It makes no difference to
147the normal operation.
148
149The default sizes for new fields are taken from the defaults
150table, which again is completely compatible. The only
151difference is that the zeros are stored as well, and hence
152cannot be defined just by the default string, but need an editor
153that specifies the sizes.
154
diff --git a/src/hdb/buffer.h b/src/hdb/buffer.h
new file mode 100644
index 0000000..9383753
--- /dev/null
+++ b/src/hdb/buffer.h
@@ -0,0 +1,38 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef BUFFERH
21#define BUFFERH
22
23#include <mt/mstring.h>
24#include <time.h>
25
26inline void LoadBuffer(mstring& buffer,const mstring& path)
27 throw(merror_t)
28{
29 if(buffer.load(path.c_str())<0) THROW("file: Could not load "<<path);
30}
31
32inline void SaveBuffer(const sref& buffer,const mstring& path,int prot=0600)
33 throw(merror_t)
34{
35 if(buffer.save(path,prot)<0) THROW("file: Could not save "<<path);
36}
37
38#endif
diff --git a/src/hdb/comp.cc b/src/hdb/comp.cc
new file mode 100644
index 0000000..3cb304a
--- /dev/null
+++ b/src/hdb/comp.cc
@@ -0,0 +1,78 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21using namespace std;
22
23const mstring compat="(compatibility)";
24const int zkey=0;
25
26OP2_IMPL(lti) {out<<(atoi(L)<atoi(R));}
27OP2_IMPL(lei) {out<<(atoi(L)<=atoi(R));}
28OP2_IMPL(gei) {out<<(atoi(L)>=atoi(R));}
29OP2_IMPL(gti) {out<<(atoi(L)>atoi(R));}
30
31void dbmap::setup()
32{
33 MLOCK(mutex);
34 if(M.empty()) {
35 opAddComp(M);
36 OP_ADDN(20,lti,lt.int,compat);
37 OP_ADDN(20,lei,le.int,compat);
38 OP_ADDN(20,gei,ge.int,compat);
39 OP_ADDN(20,gti,gt.int,compat);
40
41 M.add(new op_t("lt.case",20,M.get(sref("lt"))->f2,compat));
42 M.add(new op_t("gt.case",20,M.get(sref("gt"))->f2,compat));
43 M.add(new op_t("lt.nocase",20,M.get(sref("ltn"))->f2,compat));
44 M.add(new op_t("gt.nocase",20,M.get(sref("gtn"))->f2,compat));
45 }
46}
47
48const op_t* dbmap::CompOperator(const sref& s)
49{
50 if(M.empty()) setup();
51 const op_t* p=M.get(s);
52 if(!p) THROW("dbmap: could not find operator "<<s);
53 return p;
54}
55
56///////////////////////////////////////////////////////////////////
57
58void dbmap::Sort(const sref& tab,const sref& cmp) throw(merror_t)
59{
60 check();
61 int DO=0;
62 if(cmp.nempty()&&cmp!=compname) { compname=cmp; DO=1; }
63 if(tab.nempty()) { int col=Index(tab); if(col!=index) { index=col; DO=1; } }
64 if(DO) remap_recs();
65}
66
67void dbmap::Order(const sref& tab,const sref& cmp) throw(merror_t)
68{
69 check();
70 if(!wok) THROW("db: cannot order - readonly");
71 Sort(tab,cmp);
72 {
73 HEADLOCK(file);
74 file->head()->setcomp(index,compname);
75 remap_tabs();
76 }
77}
78
diff --git a/src/hdb/cvt.cc b/src/hdb/cvt.cc
new file mode 100644
index 0000000..4788bf1
--- /dev/null
+++ b/src/hdb/cvt.cc
@@ -0,0 +1,83 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21using namespace std;
22
23const mstring HEAD10="HTC DB File Format version 1.0\n";
24const mstring HEAD11="HDB/1.1";
25
26static fp_stream merr(stderr);
27
28static void Read(FILE* fp,mstring& s,int version) {
29 int n; fread(&n,4,1,fp);
30 switch(version) {
31 case 10:
32 s.resize(n);
33 fread(s.data(),1,n,fp);
34 break;
35 case 11:
36 s.resize(n-1);
37 fread(s.data(),1,n,fp);
38 s.resize(strlen(s.data()));
39 if(s.size()>=n) THROW("dbmap: assertion failed on dimension");
40 break;
41 default:
42 THROW("read: bad HDB version");
43 break;
44 }
45}
46
47void Convert(dbmap& db,const char* path)
48{
49 FILE* fp=fopen(path,"rb");
50 char line[1024];
51 fgets(line,1000,fp);
52 int version=0;
53 if(sref(line)==HEAD10) { fgetc(fp); version=10; }
54 else if(First(sref(line))==HEAD11) { version=11; }
55 if(version==0) THROW("hdbcvt: unknown HDB format");
56 int tables;
57 mstring s;
58 fread(&tables,4,1,fp);
59 for(int i=0;i<tables;i++) {
60 Read(fp,s,version);
61 db.InsertTable(i,s);
62 }
63 fread(&tables,4,1,fp);
64 if(tables!=db.Tables()) THROW("hdbcvt: bad table num\n");
65 spile defs(tables);
66 for(int i=0;i<tables;i++) Read(fp,defs[i],version);
67 merr<<"db: NOTE - default values no longer supported\n";
68 int index;
69 fread(&index,4,1,fp);
70 mstring compname;
71 Read(fp,compname,version);
72 db.Order(db.Table(index),compname);
73 int records;
74 fread(&records,4,1,fp);
75 for(int i=0;i<records;i++) {
76 int n; fread(&n,4,1,fp);
77 if(n!=tables) THROW("hdbcvt: bad table num\n");
78 spile rec(n);
79 for(int j=0;j<n;j++) Read(fp,rec[j],version);
80 int ok=db.Insert(rec,1);
81 if(ok<0) merr<<"hdbcvt: bad index ["<<rec[index]<<"] - not unique; skipped\n";
82 }
83}
diff --git a/src/hdb/db.cc b/src/hdb/db.cc
new file mode 100644
index 0000000..0cc9827
--- /dev/null
+++ b/src/hdb/db.cc
@@ -0,0 +1,276 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21#include <new>
22
23filemap* dbmap::flist;
24mutex_t dbmap::mutex;
25opmap dbmap::M;
26
27///////////////////////////////////////////////////////////////////////
28
29dbmap::dbmap():
30 wok(0),fatal(0),index(0),
31 compname(),file(0),tmap(),recs()
32{
33 minor=0;
34 major=0;
35}
36
37dbmap::~dbmap()
38{
39 Close();
40}
41
42///////////////////////////////////////////////////////////////////////
43
44void dbmap::remap_tabs()
45{
46 fatal=head()->fatal.get();
47 tmap.clear();
48 for(int i=0;i<Tables();i++) tmap.add(sref(head()->tabs[i]),i);
49}
50
51void dbmap::remap_recs()
52{
53 major=head()->major.get();
54 recs.clear();
55 int p=file->recs();
56 while(file->size(p)) {
57 record_t* rec=(record_t*)file->at(p);
58 if(rec->caps.get())
59 recs.push_back(idx_t(rec->at(index),rec));
60 p+=rec->mem.get();
61 }
62 recs.sort(CompOperator(compname));
63}
64
65int dbmap::remap()
66{
67 int state=0;
68 if(fatal!=head()->fatal.get()) { remap_tabs(); state|=4; }
69 if(major!=head()->major.get()) { remap_recs(); state|=2; }
70 if(minor!=head()->minor.get()) { minor=head()->minor.get(); state|=1; }
71 return state;
72}
73
74///////////////////////////////////////////////////////////////////////
75
76void Open(const mstring& path,int update=0,int prot=0600) throw(merror_t);
77void Saveas(const mstring& path,int prot=0600) throw(merror_t);
78void Close();
79
80void dbmap::Open(const mstring& path,int update,int prot) throw(merror_t)
81{
82 Close();
83 wok=update?1:0;
84 {
85 MLOCK(mutex);
86 for(filemap* p=flist;p;p=p->next)
87 if(p->Pathname()==path&&p->writable()==wok) { (file=p)->pushref(); break; }
88 if(!file) {
89 filemap* fm=new filemap(flist);
90 try { fm->open(path,wok,prot); fm->check(); file=flist=fm; }
91 catch(...) { delete fm; wok=0; throw; }
92 }
93 }
94 index=head()->index.get();
95 compname=head()->compname;
96 remap_tabs();
97 remap_recs();
98 minor=head()->minor.get();
99}
100
101void dbmap::Saveas(const mstring& path,int prot) throw(merror_t)
102{
103 file->save(path,prot);
104}
105
106void dbmap::Close()
107{
108 wok=0;
109 minor=0;
110 major=0;
111 fatal=0;
112 index=0;
113 compname.clear();
114 recs.clear();
115 tmap.clear();
116 if(file) {
117 MLOCK(mutex);
118 if(file->reference()) file->popref();
119 else {
120 if(flist==file) flist=file->next;
121 else for(filemap* p=flist;p;p=p->next)
122 if(p->next==file) { p->next=file->next; break; }
123 delete file;
124 }
125 file=0;
126 }
127}
128
129void dbmap::Sync()
130{
131 MLOCK(mutex);
132 for(filemap* p=flist;p;p=p->next) {
133 if(p->writable()) p->sync();
134 }
135}
136
137///////////////////////////////////////////////////////////////////////
138
139void dbmap::SetTable(int col,const sref& name) throw(merror_t)
140{
141 check();
142 if(!wok) THROW("db: cannot set table - readonly");
143 if(col<0||col>=Tables()) THROW("db: bad column");
144 HEADLOCK(file);
145 head()->set(col,name);
146 remap();
147}
148
149void dbmap::InsertTable(int col,const sref& name) throw(merror_t)
150{
151 check();
152 if(!wok) THROW("db: cannot insert table - readonly");
153 if(Tables()+1>file->maxtabs) THROW("db: too many tables");
154 if(col<0||col>Tables()) THROW("db: bad column");
155 {
156 HEADLOCK(file);
157 head()->insert(col,name);
158 int p=file->recs();
159 while(file->size(p)) {
160 record_t* rec=(record_t*)file->at(p);
161 if(rec->caps.get()) rec->insert(col);
162 p+=rec->mem.get();
163 }
164 head()->major.add(1);
165 }
166 index=head()->index.get();
167 compname=head()->compname;
168 remap();
169}
170
171void dbmap::RemTable(int col) throw(merror_t)
172{
173 check();
174 if(!wok) THROW("db: cannot remove table - readonly");
175 if(col<0||col>=Tables()) THROW("db: bad column");
176 if(col==head()->index.get()) THROW("db: cannot remove index table");
177 {
178 HEADLOCK(file);
179 head()->remove(col);
180 int p=file->recs();
181 while(file->size(p)) {
182 record_t* rec=(record_t*)file->at(p);
183 if(rec->caps.get()) rec->remove(col);
184 p+=rec->mem.get();
185 }
186 head()->major.add(1);
187 }
188 index=head()->index.get();
189 compname=head()->compname;
190 remap();
191}
192
193///////////////////////////////////////////////////////////////////////
194
195void dbmap::Get(int row,spile& list) throw(merror_t)
196{
197 check();
198 if(row<0||row>=recs.size()) THROW("db: not a valid record");
199 list.resize(Tables());
200 recs[row].rec->get(list);
201}
202
203void dbmap::Set(int row,int col,const sref& s) throw(merror_t)
204{
205 check();
206 if(!wok) THROW("db: cannot set - readonly");
207 if(row<0||row>=recs.size()) THROW("db: not a valid record");
208 if(col==index) THROW("db: cannot set index");
209 HEADLOCK(file);
210 record_t* rec=recs[row].rec;
211 int dim=rec->setreq(col,s);
212 if(dim>rec->caps.get()) {
213 rec=new(file->add(dim)) record_t(*rec);
214 file->rem(recs[row].rec);
215 recs[row].rec=rec;
216 head()->major.add(1);
217 major++;
218 }
219 rec->set(col,s);
220 head()->minor.add(1);
221 minor++;
222}
223
224void dbmap::Set(int row,const spile& list) throw(merror_t)
225{
226 check();
227 if(!wok) THROW("db: cannot set - readonly");
228 if(row<0||row>=recs.size()) THROW("db: not a valid record");
229 if(list.size()!=Tables())
230 THROW("db: wrong number of tables - "<<list.size()<<" should be "<<Tables());
231 HEADLOCK(file);
232 record_t* rec=recs[row].rec;
233 if(rec->at(index)!=list[index]) THROW("db: cannot set index");
234 int dim=record_t::req(list);
235 if(dim>rec->caps.get()) {
236 rec=new(file->add(dim)) record_t(*rec);
237 file->rem(recs[row].rec);
238 recs[row].rec=rec;
239 head()->major.add(1);
240 major++;
241 }
242 rec->set(list);
243 head()->minor.add(1);
244 minor++;
245}
246
247///////////////////////////////////////////////////////////////////////
248
249void dbmap::Rem(int row) throw(merror_t)
250{
251 if(!wok) THROW("db: cannot delete - readonly");
252 if(row<0||row>=recs.size()) THROW("db: not a valid record");
253 HEADLOCK(file);
254 file->rem(recs[row].rec);
255 recs.erase(recs.begin()+row);
256 head()->major.add(1);
257 major++;
258}
259
260int dbmap::Insert(const spile& list,int unique) throw(merror_t)
261{
262 check();
263 if(!wok) THROW("db: cannot insert - readonly");
264 if(index!=head()->index.get())
265 THROW("db: can only insert under normal order");
266 HEADLOCK(file);
267 remap();
268 int row=recs.get(list[index]);
269 if(row<0) {
270 record_t* rec=new(file->add(record_t::req(list))) record_t(list);
271 row=recs.add(rec->at(index),rec);
272 head()->major.add(1);
273 major++;
274 }
275 return row;
276}
diff --git a/src/hdb/db.h b/src/hdb/db.h
new file mode 100644
index 0000000..6621944
--- /dev/null
+++ b/src/hdb/db.h
@@ -0,0 +1,129 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef DBH
21#define DBH
22
23#include <hdb/sortable.h>
24#include <mt/msmap.h>
25
26////////////////////////////////////////////////////
27//
28// OK for MT and MP
29//
30
31class dbmap {
32public:
33 dbmap();
34 ~dbmap();
35
36 filemap::head_t* head() { return file->head(); }
37 const filemap::head_t* head() const { return file->head(); }
38
39 int remap();
40
41 int size() const { return recs.size(); }
42 int empty() const { return recs.empty(); }
43
44 const sref operator()(int row,int col) const throw(merror_t) {
45 check(); return recs[row].rec->at(col);
46 }
47 const sref at(int row,int col) const throw(merror_t) {
48 check(); return recs[row].rec->at(col);
49 }
50
51 ///////////////////////////////////////////////////////
52
53 void Get(int row,spile& list) throw(merror_t);
54 void Set(int row,int col,const sref& s) throw(merror_t);
55 void Set(int row,const spile& list) throw(merror_t);
56
57 ///////////////////////////////////////////////////////
58
59 void Rem(int row) throw(merror_t);
60 int Insert(const spile& list,int unique) throw(merror_t);
61
62 ///////////////////////////////////////////////////////
63
64 int Tables() const { return head()->tables.get(); }
65 const sref Table(int col) const { check(); return head()->get(col); }
66
67 ///////////////////////////////////////////////////////
68
69 void SetTable(int col,const sref& name) throw(merror_t);
70 void RemTable(int col) throw(merror_t);
71 void InsertTable(int col,const sref& name) throw(merror_t);
72
73 ///////////////////////////////////////////////////////
74
75 int Index() const { return index; }
76 int FindIndex(const sref& tab) const { return tmap.get(tab); }
77 int Index(const sref& tab) const throw(merror_t) {
78 int p=FindIndex(tab);
79 if(p<0) THROW("db: table ["<<tab<<"] does not exist");
80 return p;
81 }
82
83 ///////////////////////////////////////////////////////
84
85 const mstring& Compname() const { return compname; }
86
87 void Sort(const sref& tab,const sref& cmp) throw(merror_t);
88 void Order(const sref& tab,const sref& cmp) throw(merror_t);
89
90 int Find(const sref& key) const { return recs.get(key); }
91
92 ///////////////////////////////////////////////////////
93
94 void Open(const mstring& path,int update=0,int prot=0600) throw(merror_t);
95 void Saveas(const mstring& path,int prot=0600) throw(merror_t);
96 time_t Lastmod() const { return file->Lastmod(); }
97 void Close();
98
99 static void Sync();
100
101protected:
102 dbmap(const dbmap& db) {}
103 void operator=(const dbmap& db) {}
104
105 void check() const {
106 if(fatal!=head()->fatal.get()) THROW("db: fatal state change")
107 }
108
109 void remap_recs();
110 void remap_tabs();
111
112 static void setup();
113 static const op_t* CompOperator(const sref& s);
114
115 static filemap* flist;
116 static mutex_t mutex;
117 static opmap M;
118private:
119 int wok,minor,major,fatal,index;
120 mstring compname;
121 filemap* file;
122 rimap tmap;
123 indexmap recs;
124};
125
126void Convert(dbmap& db,const char* path);
127
128#endif
129
diff --git a/src/hdb/filemap.cc b/src/hdb/filemap.cc
new file mode 100644
index 0000000..b40644f
--- /dev/null
+++ b/src/hdb/filemap.cc
@@ -0,0 +1,302 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/filemap.h>
21#include <mt/split.h>
22#include <fcntl.h>
23#include <sys/stat.h>
24#include <sys/mman.h>
25#include <new>
26using namespace std;
27
28const int PAGESIZE=getpagesize();
29const filemap::char32 HEADNAME="HDB/1.2\n";
30const filemap::char32 COMPNAME="lt";
31const mstring dev_tag="/dev/";
32
33const int TIMEOUT=20;
34
35///////////////////////////////////////////////////
36
37filemap::head_t::head_t()
38{
39 memcpy(ident,HEADNAME,sizeof(char32));
40 lock.set(0);
41 minor.set(0);
42 major.set(0);
43 fatal.set(0);
44 filesize.set(PAGESIZE);
45 filetop.set(sizeof(head_t));
46 tables.set(0);
47 index.set(0);
48 memcpy(compname,COMPNAME,sizeof(char32));
49 memset(tabs,0,maxtabs*sizeof(char32));
50 frame.set(sizeof(head_t));
51}
52
53filemap::head_t::head_t(const head_t* hd,int mem)
54{
55 memcpy(ident,HEADNAME,sizeof(char32));
56 lock.set(0);
57 minor.set(0);
58 major.set(0);
59 fatal.set(0);
60 filetop.set(sizeof(head_t)+mem);
61 filesize.set(PAGESIZE*int((filetop.get()+PAGESIZE)/PAGESIZE));
62 tables=hd->tables;
63 index=hd->index;
64 memcpy(compname,hd->compname,sizeof(char32));
65 memset(tabs,0,maxtabs*sizeof(char32));
66 for(int i=0;i<tables.get();i++)
67 memcpy(tabs[i],hd->tabs[i],sizeof(char32));
68 frame.set(sizeof(head_t));
69}
70
71void filemap::head_t::setcomp(int idx,const sref& s)
72{
73 index.set(idx);
74 s.copyto(compname,sizeof(char32));
75 fatal.add(1);
76}
77
78void filemap::head_t::set(int col,const sref& s)
79{
80 s.copyto(tabs[col],sizeof(char32));
81 fatal.add(1);
82}
83
84void filemap::head_t::insert(int col,const sref& s)
85{
86 for(int i=maxtabs-1;i>col;i--)
87 memcpy(tabs[i],tabs[i-1],sizeof(char32));
88 if(index.get()>=col&&index.get()<tables.get())
89 index.add(1);
90 s.copyto(tabs[col],sizeof(char32));
91 tables.add(1);
92 fatal.add(1);
93}
94
95void filemap::head_t::remove(int col)
96{
97 if(index.get()!=col) {
98 for(int i=col;i<maxtabs-1;i++)
99 memcpy(tabs[i],tabs[i+1],sizeof(char32));
100 if(index.get()>col) index.set(index.get()-1);
101 tables.add(-1);
102 fatal.add(1);
103 }
104}
105
106///////////////////////////////////////////////////////
107
108filemap::chunk_t::chunk_t(chunk_t* p,int fd,int wok,int off,int size)
109{
110 next=p;
111 end=(begin=off)+size;
112 map=(char*)mmap(0,size,PROT_READ|(wok?PROT_WRITE:0),MAP_SHARED,fd,off);
113 if(map==MAP_FAILED) { map=0; THROW("filemap: could not map chunk") }
114}
115
116filemap::chunk_t::~chunk_t()
117{
118 if(map) munmap(map,end-begin);
119}
120
121int filemap::chunk_t::fill(int off)
122{
123 if(own(off)) {
124 at(off)->mem.set(end-off);
125 at(off)->caps.set(0);
126 return end;
127 }
128 else return off;
129}
130
131void filemap::chunk_t::sync()
132{
133 msync(map,end-begin,MS_SYNC);
134}
135
136///////////////////////////////////////////////////////
137
138void filemap::remap()
139{
140 while(mem->end<hd->filesize.get()) {
141 mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1);
142 }
143}
144
145filemap::rec_t* filemap::add(int n)
146{
147 int caps=sizeof(rec_t),dim=caps+n;
148 while(caps<dim) caps<<=1;
149 remap();
150 if(hd->filesize.get()<mem->end) {
151 ftruncate(fd,mem->end);
152 hd->filesize.set(mem->end);
153 }
154 while(hd->filetop.get()+caps>mem->end) {
155 hd->filetop.set(mem->fill(hd->filetop.get()));
156 mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1);
157 ftruncate(fd,mem->end);
158 hd->filesize.set(mem->end);
159 }
160 rec_t* rec=at(hd->filetop.get());
161 hd->filetop.add(caps);
162 rec->mem.set(caps);
163 rec->caps.set(n);
164 return rec;
165}
166
167void filemap::rem(rec_t* rec)
168{
169 rec->caps.set(0);
170}
171
172///////////////////////////////////////////////////////
173
174filemap::filemap(filemap* p):
175 next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0)
176{
177 memset(&mutex,0,sizeof(mutex_t));
178}
179
180filemap::filemap(filemap* p,const mstring& path,int update,int prot):
181 next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0)
182{
183 memset(&mutex,0,sizeof(mutex_t));
184 open(path,update,prot);
185}
186
187filemap::~filemap()
188{
189 close();
190}
191
192///////////////////////////////////////////////////////
193
194void filemap::open(const mstring& path,int update,int prot) throw(merror_t)
195{
196 close();
197 if(update) {
198 if(path.left(5)!=dev_tag) nodelock=new nodelock_t(path);
199 else nodelock=0;
200 wok=1;
201 }
202 else {
203 nodelock=0;
204 wok=0;
205 }
206 fd=::open(path.c_str(),wok?O_RDWR|O_CREAT:O_RDONLY,prot);
207 if(fd<0) THROW("filemap: could not open "<<path);
208 int fsize=lseek(fd,0,SEEK_END);
209 int mapsize=PAGESIZE*int((fsize+PAGESIZE)/PAGESIZE);
210 hd=(head_t*)(mem=new chunk_t(0,fd,wok,0,mapsize))->map;
211 if(fsize<sizeof(head_t)) {
212 if(wok) { ftruncate(fd,fsize=mapsize); new(hd) head_t(); }
213 else THROW("filemap: not a HDB file");
214 }
215 if(First(sref(hd->ident))!=First(sref(HEADNAME))) THROW("filemap: bad version");
216 remap();
217 if(fsize!=hd->filesize.get())
218 THROW("filemap: "<<path<<" has bad filesize - run hdbfix");
219 pathname=path;
220}
221
222void filemap::check() throw(merror_t)
223{
224 // Make a consistency test, but NEVER change filetop!!!
225 // Severe loss of data may occur if you do
226 // Use hdbfix in case the db seems corrupted
227 int p=recs(),ft=hd->filetop.get();
228 while(size(p)) {
229 rec_t* rec=at(p);
230 p+=rec->mem.get();
231 }
232 if(p<ft) THROW("filemap: "<<pathname<<" is corrupted - run hdbfix");
233 // OK, at least no data is missing. Usually p==ft,
234 // but since no locking is done here, another process
235 // may make changes that turn p>ft.
236}
237
238void filemap::save(const mstring& path,int prot) throw(merror_t)
239{
240 if(path==pathname) THROW("filemap: cannot overwrite current file");
241 int mfd=::open(path.c_str(),O_RDWR|O_CREAT|O_TRUNC,prot);
242 if(mfd<0) THROW("filemap: could not open "<<path<<" for writing");
243 int caps=0,p=recs();
244 while(size(p)) {
245 rec_t* rec=at(p);
246 if(rec->caps.get()) caps+=rec->mem.get();
247 p+=rec->mem.get();
248 }
249 head_t mhd(hd,caps);
250 ::write(mfd,&mhd,sizeof(head_t));
251 p=recs();
252 while(size(p)) {
253 rec_t* rec=at(p);
254 if(rec->caps.get()) ::write(mfd,rec,rec->mem.get());
255 p+=rec->mem.get();
256 }
257 ftruncate(mfd,mhd.filesize.get());
258 ::close(mfd);
259}
260
261void filemap::close()
262{
263 while(mem) { chunk_t* p=mem->next; delete mem; mem=p; }
264 hd=0;
265 wok=0;
266 refs=0;
267 if(fd>=0) { ::close(fd); fd=-1; }
268 delete nodelock; nodelock=0;
269 pathname.clear();
270}
271
272void filemap::sync()
273{
274 for(chunk_t* p=mem;p;p=p->next) p->sync();
275}
276
277///////////////////////////////////////////////////////
278
279void filemap::lock()
280{
281 if(hd) {
282 MLOCK(mutex);
283 if((volatile long&)hd->lock) {
284 time_t end=time(0)+TIMEOUT;
285 while((volatile long&)hd->lock&&time(0)<end);
286 }
287 hd->lock.set(1);
288 }
289}
290
291void filemap::unlock()
292{
293 if(hd) hd->lock.set(0);
294}
295
296///////////////////////////////////////////////////////
297
298time_t filemap::Lastmod() const
299{
300 struct stat fs; fstat(fd,&fs); return fs.st_mtime;
301}
302
diff --git a/src/hdb/filemap.h b/src/hdb/filemap.h
new file mode 100644
index 0000000..d6af7eb
--- /dev/null
+++ b/src/hdb/filemap.h
@@ -0,0 +1,196 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef FILEMAPH
21#define FILEMAPH
22
23#include <mt/mstring.h>
24#include <mt/lock.h>
25#include <mt/mlock.h>
26#include <mt/mlock.h>
27#include <netinet/in.h>
28
29//////////////////////////////////////////////
30//
31// NOTE that filemap is MT unsafe:
32// it needs to be called from dbmap
33//
34// The exception is when filelocking;
35// that's MT safe per filemap, and hence
36// should not be locked further
37//
38
39#define HEADLOCK(fm) headlock_t headlock(fm)
40
41//////////////////////////////////////////////////////
42
43// network byteorder
44class byte4 {
45public:
46 byte4() {} // must be uninitialized
47 explicit byte4(int h): n(htonl(h)) {}
48 void set(int h) { n=htonl(h); }
49 void add(int h) { n=htonl(ntohl(n)+h); }
50 int get() const { return ntohl(n); }
51private:
52 long n;
53};
54
55//////////////////////////////////////////////////////
56
57class filemap {
58public:
59 typedef char char32[32];
60 static const int maxtabs=32;
61
62 ////////////////////////////////////////////////////
63
64 struct head_t {
65 char32 ident;
66 byte4 lock;
67 byte4 minor;
68 byte4 major;
69 byte4 fatal;
70 byte4 filesize;
71 byte4 filetop;
72 byte4 tables;
73 byte4 index;
74 char32 compname;
75 char32 tabs[maxtabs];
76 byte4 frame;
77
78 head_t();
79 head_t(const head_t* hd,int mem);
80
81 void setcomp(int idx, const sref& cp);
82
83 sref get(int col) const { return sref(tabs[col]); }
84 void set(int col,const sref& s);
85 void insert(int col,const sref& s);
86 void remove(int col);
87 };
88
89 head_t* head() { return hd; }
90 const head_t* head() const { return hd; }
91
92 ///////////////////////////////////////////////////////
93
94 struct rec_t {
95 byte4 mem;
96 byte4 caps;
97 };
98
99 rec_t* at(int off) {
100 for(chunk_t* p=mem;p;p=p->next) if(p->own(off)) return p->at(off); return 0;
101 }
102 const rec_t* at(int off) const {
103 for(const chunk_t* p=mem;p;p=p->next) if(p->own(off)) return p->at(off);
104 return 0;
105 }
106
107 int recs() const { return hd->frame.get(); }
108 int size(int off) const {
109 if(off<hd->filetop.get()) {
110 for(const chunk_t* p=mem;p;p=p->next)
111 if(p->own(off)) return p->at(off)->mem.get();
112 }
113 return 0;
114 }
115
116 ///////////////////////////////////////////////////////
117
118 void remap();
119
120 rec_t* add(int n);
121 void rem(rec_t* rec);
122
123 ///////////////////////////////////////////////////////
124
125 filemap(filemap* p);
126 filemap(filemap* p,const mstring& path,int update=0,int prot=0600);
127 ~filemap();
128
129 filemap* next;
130
131 ///////////////////////////////////////////////////////
132
133 void open(const mstring& path,int update=0,int prot=0600) throw(merror_t);
134 void save(const mstring& path,int prot=0600) throw(merror_t);
135 void check() throw(merror_t);
136 void close();
137 void sync();
138
139 const mstring& Pathname() const { return pathname; }
140 time_t Lastmod() const;
141
142 int reference() const { return refs; }
143 int writable() const { return wok; }
144
145 void pushref() { refs++; }
146 void popref() { refs--; }
147
148 ///////////////////////////////////////////////////////
149
150 void lock();
151 void unlock();
152
153 ///////////////////////////////////////////////////////
154
155protected:
156 struct chunk_t {
157 chunk_t* next;
158 long begin;
159 long end;
160 char* map;
161
162 int size() const { return end-begin; }
163
164 chunk_t(chunk_t* p,int fd,int wok,int off,int size);
165 ~chunk_t();
166
167 int own(int off) const { return off>=begin&&off<end; }
168 void sync();
169
170 rec_t* at(int off) { return (rec_t*)(map+(off-begin)); }
171 const rec_t* at(int off) const { return (rec_t*)(map+(off-begin)); }
172
173 int fill(int off);
174 };
175private:
176 int fd,wok,refs;
177 mstring pathname;
178 head_t* hd;
179 chunk_t* mem;
180 mutex_t mutex;
181 nodelock_t* nodelock;
182private:
183 filemap(const filemap&) {}
184 void operator=(const filemap&) {}
185};
186
187//////////////////////////////////////////////////////
188
189struct headlock_t {
190 filemap* fm;
191 headlock_t(filemap* f) : fm(f) { fm->lock(); }
192 ~headlock_t() { if(fm) fm->unlock(); }
193};
194
195#endif
196
diff --git a/src/hdb/hdbcvt.cc b/src/hdb/hdbcvt.cc
new file mode 100644
index 0000000..ece70f0
--- /dev/null
+++ b/src/hdb/hdbcvt.cc
@@ -0,0 +1,50 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21#include <mt/lock.h>
22
23static fp_stream mout(stdout),merr(stderr);
24
25main(int argc,char* argv[]) throw(merror_t) try
26{
27 if(argc<2) {
28 merr<<"Usage: "<<argv[0]<<" <db> [<index> <cp>]\n";
29 return -1;
30 }
31 mstring newfile=argv[1];
32 mstring oldfile=newfile+sref(".bak");
33 rename(newfile.c_str(),oldfile.c_str());
34 dbmap db;
35 try {
36 db.Open(oldfile);
37 if(argc>3) db.Sort(sref(argv[2]),sref(argv[3]));
38 db.Saveas(newfile);
39 }
40 catch(const merror_t& e) {
41 merr<<"Opened as newfile, old version\n";
42 db.Open(newfile,1);
43 Convert(db,oldfile.c_str());
44 }
45 return 0;
46}
47catch(const merror_t& e) {
48 merr<<e.desc<<"\n";
49 return -1;
50}
diff --git a/src/hdb/hdbfix.cc b/src/hdb/hdbfix.cc
new file mode 100644
index 0000000..b82f151
--- /dev/null
+++ b/src/hdb/hdbfix.cc
@@ -0,0 +1,140 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21
22static fp_stream mout(stdout),merr(stderr);
23
24main(int argc,char* argv[]) try
25{
26 if(argc<2) {
27 mout<<"Usage: "<<argv[0]<<" [-s] <path>\n";
28 return -1;
29 }
30 mstring path; int silent;
31 if(argc>2) { silent=1; path=argv[2]; }
32 else { silent=0; path=argv[1]; }
33 mstring map; if(map.load(path)<0) THROW("hdbfix: could not open "<<path);
34 filemap::head_t head;
35 mstring version=First(sref(head.ident,sizeof(head.ident)));
36 memcpy(&head,map.data(),sizeof(head));
37 mstring cver=First(sref(head.ident,sizeof(head.ident)));
38 mstring answer;
39 int save=0;
40 if(version!=cver) {
41 merr<<path<<": Not a "<<version<<" file\n";
42 return -1;
43 }
44 else {
45 int top=head.frame.get(),last=0;
46 while(top<head.filetop.get()&&top<map.size()) {
47 filemap::rec_t* rec=(filemap::rec_t*)(map.data()+top);
48 if(rec->mem.get()<=0) {
49 if(!silent&&rec->mem.get()<0)
50 mout<<"ending mem: "<<rec->mem.get()<<"\n";
51 break;
52 }
53 last=top;
54 top+=rec->mem.get();
55 }
56 if(top==head.filetop.get()&&head.filesize.get()==map.size()) {
57 if(!silent) mout<<path<<": OK\n";
58 }
59 else {
60 mout<<path<<": CORRUPTED\n";
61 if(silent) return 0;
62 mout<<"\n";
63 mout<<"Head info:\n";
64 mout<<"Size: "<<head.filesize.get()<<"\n";
65 mout<<"Frame: "<<head.frame.get()<<"\n";
66 mout<<"Top: "<<head.filetop.get()<<"\n";
67 mout<<"\n";
68
69 mout<<"Actual sizes:\n";
70 mout<<"Size: "<<map.size()<<"\n";
71 mout<<"Last: "<<last<<"\n";
72 mout<<"End: "<<top<<"\n";
73 mout<<"\n";
74
75 if(map.size()!=head.filesize.get()) {
76 mout<<"Filesizes differ - do you want to set the filesize to "
77 <<head.filesize.get()<<"?\n";
78 getline(answer,stdin); answer.tolower();
79 if(*answer=='y') {
80 map.resize(head.filesize.get());
81 save=1;
82 mout<<"Done.\n";
83 }
84 else {
85 mout<<"No change - quitting.\n";
86 return 0;
87 }
88 }
89
90 int nend=0,end=head.filetop.get();
91 for(int p=head.filetop.get()-4;p>=last;p-=4){
92 int val=ntohl(*(long*)(map.data()+p));
93 if(p+val==end) { nend++; end=p; }
94 }
95 int gap=end-top;
96
97 mout<<"GAP DATA\n";
98 mout<<"==============================\n";
99 for(int i=top;i<end;i++) {
100 if(cset_printable.test(map[i])) mout.put(map[i]);
101 else mout<<"["<<(int)map[i]<<"]";
102 }
103 mout<<"\n==============================\n";
104 mout<<"\n";
105
106 mout<<"At-end sequence:\n";
107 mout<<"Start: "<<end<<"\n";
108 mout<<"Gap: "<<gap<<"\n";
109 mout<<"\n";
110
111 if(gap>0) {
112 mout<<"Do you want to mark the gap as deleted?\n";
113 getline(answer,stdin); answer.tolower();
114 if(*answer=='y') {
115 filemap::rec_t* rec=(filemap::rec_t*)(map.data()+top);
116 rec->mem.set(gap);
117 rec->caps.set(0);
118 save=1;
119 mout<<"Done.\n";
120 }
121 else {
122 mout<<"No change - quitting.\n";
123 return 0;
124 }
125 }
126 }
127 }
128
129 if(save) map.save(path);
130
131 return 0;
132}
133catch(const merror_t& e) {
134 merr<<e.desc<<"\n";
135 return -1;
136}
137catch(...) {
138 merr<<"FATAL ERROR\n";
139 return -1;
140}
diff --git a/src/hdb/hdbtest.cc b/src/hdb/hdbtest.cc
new file mode 100644
index 0000000..89169da
--- /dev/null
+++ b/src/hdb/hdbtest.cc
@@ -0,0 +1,153 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21
22static fp_stream mout(stdout),merr(stderr);
23
24main(int argc,char* argv[]) try
25{
26 if(argc<2) {
27 mout<<"Usage: "<<argv[0]<<" [-s] <path>\n";
28 return -1;
29 }
30 mstring path; int silent;
31 if(argc>2) { silent=atoi(argv[1]); path=argv[2]; }
32 else { silent=0; path=argv[1]; }
33 mstring map; if(map.load(path)<0) THROW("hdbfix: could not open "<<path);
34 filemap::head_t head;
35 mstring version=First(sref(head.ident,sizeof(head.ident)));
36 memcpy(&head,map.data(),sizeof(head));
37 mstring cver=First(sref(head.ident,sizeof(head.ident)));
38 mstring answer;
39 int save=0;
40 if(version!=cver) {
41 merr<<path<<": Not a "<<version<<" file\n";
42 return -1;
43 }
44 else {
45 int top=head.frame.get(),last=0;
46 while(top<head.filetop.get()&&top<map.size()) {
47 filemap::rec_t* rec=(filemap::rec_t*)(map.data()+top);
48 if(rec->mem.get()<=0) break;
49 if(silent) {
50 if(silent==top) {
51 mout<<top<<" *************\n";
52 for(int i=0;i<65536;i++) {
53 char c=map.data()[top+i];
54 if(cset_printable.test(c)) mout.put(c);
55 }
56 mout<<"\n\n";
57 }
58 }
59 else {
60 mout<<top<<" *************\n";
61 for(int i=0;i<1024;i++) {
62 char c=map.data()[top+i];
63 if(cset_printable.test(c)) mout.put(c);
64 }
65 mout<<"\n\n";
66 }
67 last=top; top+=rec->mem.get();
68 }
69 if(top==head.filetop.get()&&head.filesize.get()==map.size()) {
70 if(!silent) mout<<path<<": OK\n";
71 }
72 else {
73 mout<<path<<": CORRUPTED\n";
74 if(silent) return 0;
75 mout<<"\n";
76 mout<<"Head info:\n";
77 mout<<"Size: "<<head.filesize.get()<<"\n";
78 mout<<"Frame: "<<head.frame.get()<<"\n";
79 mout<<"Top: "<<head.filetop.get()<<"\n";
80 mout<<"\n";
81
82 mout<<"Actual sizes:\n";
83 mout<<"Size: "<<map.size()<<"\n";
84 mout<<"Last: "<<last<<"\n";
85 mout<<"End: "<<top<<"\n";
86 mout<<"\n";
87
88 if(map.size()!=head.filesize.get()) {
89 mout<<"Filesizes differ - do you want to set the filesize to "
90 <<head.filesize.get()<<"?\n";
91 getline(answer,stdin); answer.tolower();
92 if(*answer=='y') {
93 map.resize(head.filesize.get());
94 save=1;
95 mout<<"Done.\n";
96 }
97 else {
98 mout<<"No change - quitting.\n";
99 return 0;
100 }
101 }
102
103 int nend=0,end=head.filetop.get();
104 for(int p=head.filetop.get()-4;p>=last;p-=4){
105 unsigned val=*(unsigned*)(map.data()+p);
106 if(p+val==end) { nend++; end=p; }
107 }
108 int gap=end-top;
109
110 mout<<"GAP DATA\n";
111 mout<<"==============================\n";
112 for(int i=top;i<end;i++) {
113 if(cset_printable.test(map[i])) mout.put(map[i]);
114 else if(map[i]) mout<<"["<<(int)map[i]<<"]";
115 }
116 mout<<"\n==============================\n";
117 mout<<"\n";
118
119 mout<<"At-end sequence:\n";
120 mout<<"Start: "<<end<<"\n";
121 mout<<"Gap: "<<gap<<"\n";
122 mout<<"\n";
123
124 if(gap>0) {
125 mout<<"Do you want to mark the gap as deleted?\n";
126 getline(answer,stdin); answer.tolower();
127 if(*answer=='y') {
128 filemap::rec_t* rec=(filemap::rec_t*)(map.data()+top);
129 rec->mem.set(gap);
130 rec->caps.set(0);
131 save=1;
132 mout<<"Done.\n";
133 }
134 else {
135 mout<<"No change - quitting.\n";
136 return 0;
137 }
138 }
139 }
140 }
141
142 if(save) map.save(path);
143
144 return 0;
145}
146catch(const merror_t& e) {
147 merr<<e.desc<<"\n";
148 return -1;
149}
150catch(...) {
151 merr<<"FATAL ERROR\n";
152 return -1;
153}
diff --git a/src/hdb/sortable.cc b/src/hdb/sortable.cc
new file mode 100644
index 0000000..39adf58
--- /dev/null
+++ b/src/hdb/sortable.cc
@@ -0,0 +1,81 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/sortable.h>
21#include <algorithm>
22using namespace std;
23
24int int_stream::appc(char c) { return val=c,1; }
25int int_stream::appi(int i) { return val=i,1; }
26int int_stream::appf(double f) { return val=(int)f,1; }
27int int_stream::apps(const sref& s) { return val=atoi(s),s.size(); }
28
29///////////////////////////////////////////////////
30
31record_t::record_t(const spile& list)
32{
33 pos[0].set(0);
34 for(int i=0;i<list.size();i++) {
35 memcpy(data+pos[i].get(),list[i].data(),list[i].size());
36 pos[i+1].set(pos[i].get()+list[i].size());
37 }
38 for(int i=list.size();i<maxtabs;i++) pos[i+1]=pos[i];
39}
40
41void record_t::set(const spile& list)
42{
43 pos[0].set(0);
44 for(int i=0;i<list.size();i++) {
45 memcpy(data+pos[i].get(),list[i].data(),list[i].size());
46 pos[i+1].set(pos[i].get()+list[i].size());
47 }
48 for(int i=list.size();i<maxtabs;i++) pos[i+1]=pos[i];
49}
50
51void record_t::resize(int col,int n)
52{
53 int delta=n+pos[col].get()-pos[col+1].get();
54 if(delta) {
55 memmove(data+pos[col+1].get()+delta,data+pos[col+1].get(),
56 pos[maxtabs].get()-pos[col+1].get());
57 for(int i=col+1;i<=maxtabs;i++) pos[i].add(delta);
58 }
59}
60
61///////////////////////////////////////////////////
62
63int indexmap::get(const sref& t) const
64{
65 pair<const_iterator,const_iterator> p=equal_range(begin(),end(),idx_t(t,0),comp);
66 return p.first!=p.second?p.first-begin():-1;
67}
68
69int indexmap::add(const sref& t,record_t* rec)
70{
71 idx_t tmp(t,rec);
72 pair<iterator,iterator> p=equal_range(begin(),end(),tmp,comp);
73 if(p.first!=p.second) return &(*p.first=tmp)-begin();
74 else return insert(p.second,tmp)-begin();
75}
76
77void indexmap::sort(const op_t* op)
78{
79 comp.func=op->f2;
80 ::sort(begin(),end(),comp);
81}
diff --git a/src/hdb/sortable.h b/src/hdb/sortable.h
new file mode 100644
index 0000000..7de0aa8
--- /dev/null
+++ b/src/hdb/sortable.h
@@ -0,0 +1,138 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef SORTABLEH
21#define SORTABLEH
22
23#include <hdb/filemap.h>
24#include <ops/express.h>
25#include <mt/mset.h>
26
27////////////////////////////////////////////////////
28//
29// NOTE that these classes are not MT safe:
30// need to be called from dbamp
31//
32
33struct record_t : public filemap::rec_t {
34 static const int maxtabs=filemap::maxtabs;
35 static const int recsize=(maxtabs+1)*sizeof(long);
36
37 byte4 pos[maxtabs+1];
38 char data[sizeof(byte4)];
39
40 int size() const { return pos[maxtabs].get()-pos[0].get(); }
41 int size(int i) const { return pos[i+1].get()-pos[i].get(); }
42
43 sref at(int i) const { return sref(data+pos[i].get(),size(i)); }
44
45 /////////////////////////////////////////////////
46
47 record_t(const record_t& rec) {
48 for(int i=0;i<=maxtabs;i++) pos[i]=rec.pos[i];
49 memcpy(data,rec.data,rec.caps.get());
50 }
51 record_t(const spile& list);
52
53 /////////////////////////////////////////////////
54
55 void get(spile& list) const {
56 for(int i=0;i<list.size();i++) list[i]=at(i);
57 }
58
59 /////////////////////////////////////////////////
60
61 int setreq(int col,const sref& s) const {
62 return recsize+size()+s.size()-size(col);
63 }
64 inline static int req(const spile& list) {
65 int n=0; for(int i=0;i<list.size();i++) n+=list[i].size();
66 return recsize+n;
67 }
68
69 /////////////////////////////////////////////////
70
71 void set(int col,const sref& s) {
72 resize(col,s.size());
73 memcpy(data+pos[col].get(),s.data(),s.size());
74 }
75
76 void set(const spile& list);
77
78 /////////////////////////////////////////////////
79
80 void insert(int col) {
81 for(int i=maxtabs;i>col;i--) pos[i]=pos[i-1];
82 }
83 void remove(int col) {
84 resize(col,0);
85 for(int i=col;i<maxtabs;i++) pos[i]=pos[i+1];
86 }
87
88 /////////////////////////////////////////////////
89
90 void resize(int col,int n);
91
92};
93
94////////////////////////////////////////////////////////////////////
95
96struct int_stream : public mstream {
97 int val;
98
99 int_stream() : val(0) {}
100
101 int appc(char c);
102 int appi(int i);
103 int appf(double f);
104 int apps(const sref& s);
105};
106
107////////////////////////////////////////////////////////////////////
108
109struct idx_t {
110 sref key; record_t* rec;
111 idx_t(const sref& t,record_t* p) : key(t),rec(p) {}
112};
113
114class indexmap : public mrvec<idx_t> {
115public:
116 typedef mrvec<idx_t>::iterator iterator;
117 typedef mrvec<idx_t>::const_iterator const_iterator;
118
119 struct comp_t {
120 op_t::op2_t func;
121 comp_t() : func(0) {}
122 comp_t(const op_t* op) : func(op->f2) {}
123 bool operator()(const idx_t& a,const idx_t& b) const {
124 int_stream res;
125 op_env env;
126 func(res,env,a.key,b.key);
127 return res.val;
128 }
129 };
130
131 int get(const sref& t) const;
132 int add(const sref& t,record_t* rec);
133 void sort(const op_t* op);
134private:
135 comp_t comp;
136};
137
138#endif
diff --git a/src/hdb/test.cc b/src/hdb/test.cc
new file mode 100644
index 0000000..b8d335b
--- /dev/null
+++ b/src/hdb/test.cc
@@ -0,0 +1,52 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <hdb/db.h>
21
22static fp_stream merr(stderr);
23
24main(int argc,char* argv[]) throw(merror_t) try
25{
26 if(argc<2) {
27 merr<<"Usage: "<<argv[0]<<" <path>\n";
28 return -1;
29 }
30 dbmap m;
31 m.Open(argv[1]);
32
33 merr<<m.head()->filesize.get()<<"\n";
34 merr<<m.head()->filetop.get()<<"\n";
35 merr<<m.head()->frame.get()<<"\n";
36
37 for(int j=0;j<m.Tables();j++) merr<<m.Table(j)<<" ";
38 merr<<"\n\n";
39
40 for(int i=0;i<m.size();i++) {
41 //for(int j=0;j<m.Tables();j++) merr<<m(i,j)<<" ";
42 for(int j=0;j<1;j++) merr<<m(i,j)<<" ";
43
44 merr<<"\n";
45 }
46
47 return 0;
48}
49catch(const merror_t& e) {
50 merr<<e.desc<<"\n";
51 return -1;
52}