diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2011-10-08 20:30:28 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2011-10-08 20:30:28 +0200 |
| commit | 5df79c53745fde5d6c3340a2979b1429cd5892c1 (patch) | |
| tree | 1a81af141708b826e9c61e8a04019994fcca8298 /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/README | 154 | ||||
| -rw-r--r-- | src/hdb/buffer.h | 38 | ||||
| -rw-r--r-- | src/hdb/comp.cc | 78 | ||||
| -rw-r--r-- | src/hdb/cvt.cc | 83 | ||||
| -rw-r--r-- | src/hdb/db.cc | 276 | ||||
| -rw-r--r-- | src/hdb/db.h | 129 | ||||
| -rw-r--r-- | src/hdb/filemap.cc | 302 | ||||
| -rw-r--r-- | src/hdb/filemap.h | 196 | ||||
| -rw-r--r-- | src/hdb/hdbcvt.cc | 50 | ||||
| -rw-r--r-- | src/hdb/hdbfix.cc | 140 | ||||
| -rw-r--r-- | src/hdb/hdbtest.cc | 153 | ||||
| -rw-r--r-- | src/hdb/sortable.cc | 81 | ||||
| -rw-r--r-- | src/hdb/sortable.h | 138 | ||||
| -rw-r--r-- | src/hdb/test.cc | 52 |
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 | |||
| 20 | HDB/1.2 FILE FORMAT SPECIFICATION Sat Feb 24 18:38:25 MET 2001 | ||
| 21 | |||
| 22 | MT AND MP SAFE | ||
| 23 | |||
| 24 | One single format utilizing memory maps, and which is designed to | ||
| 25 | work safely between processes and within a multi-threading process. | ||
| 26 | To accomplish this, there are two types of locking mechanisms - | ||
| 27 | one inter-process lock in the file header, and one MT lock implemented | ||
| 28 | the usual way. | ||
| 29 | |||
| 30 | The format is robust within each thread, except for changes in the | ||
| 31 | table structure, which generates fatal error_t errors. | ||
| 32 | |||
| 33 | The format includes a finite (32) number of tables, which should not | ||
| 34 | be a real problem. Besides, a possible change is to move the frame | ||
| 35 | pointer to a position inside, so that additional header could be | ||
| 36 | added if needed. | ||
| 37 | |||
| 38 | FILEFORMAT in HDB/1.2 | ||
| 39 | |||
| 40 | BYTES 1.2 NAME DESCRIPTION | ||
| 41 | 32 IDENT Identification header | ||
| 42 | 4 LOCK Inter-process lock | ||
| 43 | 4 MINOR Minor modification state number | ||
| 44 | 4 MAJOR Major modification state number | ||
| 45 | 4 FATAL Fatal modification state number | ||
| 46 | 4 FILESIZE Size of file | ||
| 47 | 4 FILETOP Size of used part | ||
| 48 | 4 TABLES Number of table name strings | ||
| 49 | 4 INDEX Which column is the order column. | ||
| 50 | 32 COMPNAME Comparison name | ||
| 51 | 32*32 TABLENAMES Table names | ||
| 52 | 4 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 | ||
| 57 | 0 ZEROS The rest of the file is padded with zeros. | ||
| 58 | |||
| 59 | The records are always staying where they land; a minor change means | ||
| 60 | a change in-place, a major change means a change leading to an index | ||
| 61 | update, which could be a deletion, an insertion. Those are updated at | ||
| 62 | will, and not seen until wanted. A fatal change is a change in the | ||
| 63 | table structure, i.e., an insertion or deletion of a table. Also a | ||
| 64 | table name change counts as fatal. The filesize used to be (earlier | ||
| 65 | versions) a state needing attention as well, but not anymore. | ||
| 66 | |||
| 67 | -- | ||
| 68 | |||
| 69 | HDB/1.11 FILE FORMAT SPECIFICATION Mon Jan 29 03:51:41 MET 2001 | ||
| 70 | |||
| 71 | STATIC/DYNAMIC mapping | ||
| 72 | |||
| 73 | The header field determines whether the mapping is dynamic or static, | ||
| 74 | i.e., static mapping creates a shared mmap and edits the records | ||
| 75 | directly, whereas the dynamic mapping creates a private map and | ||
| 76 | stores the changes on save only. | ||
| 77 | |||
| 78 | The format changed quite a lot after all... | ||
| 79 | |||
| 80 | FILEFORMAT in HDB/1.11 | ||
| 81 | |||
| 82 | BYTES 1.11 NAME DESCRIPTION | ||
| 83 | 32 IDENT Identification header | ||
| 84 | 4 MODNUM Modification number | ||
| 85 | 4 LOCK Soft lock | ||
| 86 | 4 FILESIZE Size of file | ||
| 87 | 4 FILETOP Size of used part | ||
| 88 | 4 FIXED Whether fixed mapping or not | ||
| 89 | 4 TABLES Number of table name strings | ||
| 90 | 4 INDEX Which column is the order column. | ||
| 91 | 32 COMPNAME Comparison name | ||
| 92 | 4 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 | |||
| 99 | Size info [4] always refers to the stringref-size, but the number of | ||
| 100 | bytes actually written is as follows: for fixed maps, it is tabs[].caps, | ||
| 101 | and for dynamic maps, it is capsize(), which is defined as (n=stringref-size) | ||
| 102 | (4+n)&~3. | ||
| 103 | |||
| 104 | -- | ||
| 105 | |||
| 106 | HDB/1.10 FILE FORMAT SPECIFICATION Thu Jan 25 00:42:49 MET 2001 | ||
| 107 | |||
| 108 | The HDB/1.0 format has been used widely in the software | ||
| 109 | for our group (MST) during the last three years of so. | ||
| 110 | The HDB/1.1 file format is binary compatible with 1.0, | ||
| 111 | and merely consists of a formalization, along with a | ||
| 112 | change in the internal manipulation scheme and a new | ||
| 113 | hdb editor. | ||
| 114 | |||
| 115 | FILE FORMAT IN 1.0 and 1.1 | ||
| 116 | |||
| 117 | BYTES 1.0/1.1 NAME DESCRIPTION | ||
| 118 | 32/variable HEAD Identification header. | ||
| 119 | Used to be exactly 32 bytes, | ||
| 120 | but see no point in that. | ||
| 121 | 4 TABLES Number of table name strings | ||
| 122 | 4 dim bytes in string | ||
| 123 | * data string data | ||
| 124 | 4 DEFAULTS Number of default strings | ||
| 125 | 4 dim bytes in string | ||
| 126 | * data string data | ||
| 127 | 4 ORDERBY Which column is the order column. | ||
| 128 | 4 dim Number of bytes in compname. | ||
| 129 | * data string data | ||
| 130 | 4 RECORDS Number of records in file. | ||
| 131 | 4 tables | ||
| 132 | 4 dim | ||
| 133 | * data | ||
| 134 | 0/variable FOOTER File ends. Simple Footer. | ||
| 135 | |||
| 136 | ADDITIONAL CHANGES BETWEEN 1.0 and 1.1 | ||
| 137 | |||
| 138 | The major difference - and the rationale for 1.1 - is | ||
| 139 | the possiblity to edit files directly, as in mmap. | ||
| 140 | To be able to do this, there is a need to force a minimum | ||
| 141 | size to each field. This is done by padding the data | ||
| 142 | with trailing zeros. This is ok since only string data | ||
| 143 | is stored in the fields, and never binary data. Just think | ||
| 144 | of it; if it was binary data, it would not be replaced | ||
| 145 | by something the same size anyways, so thats forbidden | ||
| 146 | in the direct-editing mode. It makes no difference to | ||
| 147 | the normal operation. | ||
| 148 | |||
| 149 | The default sizes for new fields are taken from the defaults | ||
| 150 | table, which again is completely compatible. The only | ||
| 151 | difference is that the zeros are stored as well, and hence | ||
| 152 | cannot be defined just by the default string, but need an editor | ||
| 153 | that 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 | |||
| 26 | inline 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 | |||
| 32 | inline 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> | ||
| 21 | using namespace std; | ||
| 22 | |||
| 23 | const mstring compat="(compatibility)"; | ||
| 24 | const int zkey=0; | ||
| 25 | |||
| 26 | OP2_IMPL(lti) {out<<(atoi(L)<atoi(R));} | ||
| 27 | OP2_IMPL(lei) {out<<(atoi(L)<=atoi(R));} | ||
| 28 | OP2_IMPL(gei) {out<<(atoi(L)>=atoi(R));} | ||
| 29 | OP2_IMPL(gti) {out<<(atoi(L)>atoi(R));} | ||
| 30 | |||
| 31 | void 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 | |||
| 48 | const 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 | |||
| 58 | void 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 | |||
| 67 | void 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> | ||
| 21 | using namespace std; | ||
| 22 | |||
| 23 | const mstring HEAD10="HTC DB File Format version 1.0\n"; | ||
| 24 | const mstring HEAD11="HDB/1.1"; | ||
| 25 | |||
| 26 | static fp_stream merr(stderr); | ||
| 27 | |||
| 28 | static 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 | |||
| 47 | void 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 | |||
| 23 | filemap* dbmap::flist; | ||
| 24 | mutex_t dbmap::mutex; | ||
| 25 | opmap dbmap::M; | ||
| 26 | |||
| 27 | /////////////////////////////////////////////////////////////////////// | ||
| 28 | |||
| 29 | dbmap::dbmap(): | ||
| 30 | wok(0),fatal(0),index(0), | ||
| 31 | compname(),file(0),tmap(),recs() | ||
| 32 | { | ||
| 33 | minor=0; | ||
| 34 | major=0; | ||
| 35 | } | ||
| 36 | |||
| 37 | dbmap::~dbmap() | ||
| 38 | { | ||
| 39 | Close(); | ||
| 40 | } | ||
| 41 | |||
| 42 | /////////////////////////////////////////////////////////////////////// | ||
| 43 | |||
| 44 | void 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 | |||
| 51 | void 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 | |||
| 65 | int 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 | |||
| 76 | void Open(const mstring& path,int update=0,int prot=0600) throw(merror_t); | ||
| 77 | void Saveas(const mstring& path,int prot=0600) throw(merror_t); | ||
| 78 | void Close(); | ||
| 79 | |||
| 80 | void 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 | |||
| 101 | void dbmap::Saveas(const mstring& path,int prot) throw(merror_t) | ||
| 102 | { | ||
| 103 | file->save(path,prot); | ||
| 104 | } | ||
| 105 | |||
| 106 | void 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 | |||
| 129 | void 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 | |||
| 139 | void 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 | |||
| 149 | void 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 | |||
| 171 | void 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 | |||
| 195 | void 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 | |||
| 203 | void 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 | |||
| 224 | void 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 | |||
| 249 | void 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 | |||
| 260 | int 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 | |||
| 31 | class dbmap { | ||
| 32 | public: | ||
| 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 | |||
| 101 | protected: | ||
| 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; | ||
| 118 | private: | ||
| 119 | int wok,minor,major,fatal,index; | ||
| 120 | mstring compname; | ||
| 121 | filemap* file; | ||
| 122 | rimap tmap; | ||
| 123 | indexmap recs; | ||
| 124 | }; | ||
| 125 | |||
| 126 | void 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> | ||
| 26 | using namespace std; | ||
| 27 | |||
| 28 | const int PAGESIZE=getpagesize(); | ||
| 29 | const filemap::char32 HEADNAME="HDB/1.2\n"; | ||
| 30 | const filemap::char32 COMPNAME="lt"; | ||
| 31 | const mstring dev_tag="/dev/"; | ||
| 32 | |||
| 33 | const int TIMEOUT=20; | ||
| 34 | |||
| 35 | /////////////////////////////////////////////////// | ||
| 36 | |||
| 37 | filemap::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 | |||
| 53 | filemap::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 | |||
| 71 | void 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 | |||
| 78 | void filemap::head_t::set(int col,const sref& s) | ||
| 79 | { | ||
| 80 | s.copyto(tabs[col],sizeof(char32)); | ||
| 81 | fatal.add(1); | ||
| 82 | } | ||
| 83 | |||
| 84 | void 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 | |||
| 95 | void 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 | |||
| 108 | filemap::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 | |||
| 116 | filemap::chunk_t::~chunk_t() | ||
| 117 | { | ||
| 118 | if(map) munmap(map,end-begin); | ||
| 119 | } | ||
| 120 | |||
| 121 | int 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 | |||
| 131 | void filemap::chunk_t::sync() | ||
| 132 | { | ||
| 133 | msync(map,end-begin,MS_SYNC); | ||
| 134 | } | ||
| 135 | |||
| 136 | /////////////////////////////////////////////////////// | ||
| 137 | |||
| 138 | void 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 | |||
| 145 | filemap::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 | |||
| 167 | void filemap::rem(rec_t* rec) | ||
| 168 | { | ||
| 169 | rec->caps.set(0); | ||
| 170 | } | ||
| 171 | |||
| 172 | /////////////////////////////////////////////////////// | ||
| 173 | |||
| 174 | filemap::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 | |||
| 180 | filemap::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 | |||
| 187 | filemap::~filemap() | ||
| 188 | { | ||
| 189 | close(); | ||
| 190 | } | ||
| 191 | |||
| 192 | /////////////////////////////////////////////////////// | ||
| 193 | |||
| 194 | void 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 | |||
| 222 | void 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 | |||
| 238 | void 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 | |||
| 261 | void 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 | |||
| 272 | void filemap::sync() | ||
| 273 | { | ||
| 274 | for(chunk_t* p=mem;p;p=p->next) p->sync(); | ||
| 275 | } | ||
| 276 | |||
| 277 | /////////////////////////////////////////////////////// | ||
| 278 | |||
| 279 | void 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 | |||
| 291 | void filemap::unlock() | ||
| 292 | { | ||
| 293 | if(hd) hd->lock.set(0); | ||
| 294 | } | ||
| 295 | |||
| 296 | /////////////////////////////////////////////////////// | ||
| 297 | |||
| 298 | time_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 | ||
| 44 | class byte4 { | ||
| 45 | public: | ||
| 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); } | ||
| 51 | private: | ||
| 52 | long n; | ||
| 53 | }; | ||
| 54 | |||
| 55 | ////////////////////////////////////////////////////// | ||
| 56 | |||
| 57 | class filemap { | ||
| 58 | public: | ||
| 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 | |||
| 155 | protected: | ||
| 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 | }; | ||
| 175 | private: | ||
| 176 | int fd,wok,refs; | ||
| 177 | mstring pathname; | ||
| 178 | head_t* hd; | ||
| 179 | chunk_t* mem; | ||
| 180 | mutex_t mutex; | ||
| 181 | nodelock_t* nodelock; | ||
| 182 | private: | ||
| 183 | filemap(const filemap&) {} | ||
| 184 | void operator=(const filemap&) {} | ||
| 185 | }; | ||
| 186 | |||
| 187 | ////////////////////////////////////////////////////// | ||
| 188 | |||
| 189 | struct 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 | |||
| 23 | static fp_stream mout(stdout),merr(stderr); | ||
| 24 | |||
| 25 | main(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 | } | ||
| 47 | catch(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 | |||
| 22 | static fp_stream mout(stdout),merr(stderr); | ||
| 23 | |||
| 24 | main(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 | } | ||
| 133 | catch(const merror_t& e) { | ||
| 134 | merr<<e.desc<<"\n"; | ||
| 135 | return -1; | ||
| 136 | } | ||
| 137 | catch(...) { | ||
| 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 | |||
| 22 | static fp_stream mout(stdout),merr(stderr); | ||
| 23 | |||
| 24 | main(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 | } | ||
| 146 | catch(const merror_t& e) { | ||
| 147 | merr<<e.desc<<"\n"; | ||
| 148 | return -1; | ||
| 149 | } | ||
| 150 | catch(...) { | ||
| 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> | ||
| 22 | using namespace std; | ||
| 23 | |||
| 24 | int int_stream::appc(char c) { return val=c,1; } | ||
| 25 | int int_stream::appi(int i) { return val=i,1; } | ||
| 26 | int int_stream::appf(double f) { return val=(int)f,1; } | ||
| 27 | int int_stream::apps(const sref& s) { return val=atoi(s),s.size(); } | ||
| 28 | |||
| 29 | /////////////////////////////////////////////////// | ||
| 30 | |||
| 31 | record_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 | |||
| 41 | void 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 | |||
| 51 | void 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 | |||
| 63 | int 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 | |||
| 69 | int 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 | |||
| 77 | void 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 | |||
| 33 | struct 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 | |||
| 96 | struct 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 | |||
| 109 | struct idx_t { | ||
| 110 | sref key; record_t* rec; | ||
| 111 | idx_t(const sref& t,record_t* p) : key(t),rec(p) {} | ||
| 112 | }; | ||
| 113 | |||
| 114 | class indexmap : public mrvec<idx_t> { | ||
| 115 | public: | ||
| 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); | ||
| 134 | private: | ||
| 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 | |||
| 22 | static fp_stream merr(stderr); | ||
| 23 | |||
| 24 | main(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 | } | ||
| 49 | catch(const merror_t& e) { | ||
| 50 | merr<<e.desc<<"\n"; | ||
| 51 | return -1; | ||
| 52 | } | ||
