Imported Upstream version 5.3.21
[platform/upstream/libdb.git] / lang / csharp / src / MultipleDatabaseEntry.cs
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2009, 2012 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 using System;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Text;
11 using BerkeleyDB.Internal;
12
13 namespace BerkeleyDB {
14     /// <summary>
15     /// A class providing access to multiple <see cref="DatabaseEntry"/>
16     /// objects.
17     /// </summary>
18     public class MultipleDatabaseEntry : DatabaseEntry, 
19         IEnumerable<DatabaseEntry> {
20         private bool _recno = false;
21
22         internal MultipleDatabaseEntry(DatabaseEntry dbt) {
23             _data = dbt.UserData;
24             ulen = dbt.ulen;
25         }
26
27         /// <summary>
28         /// Construct a new key or data bulk buffer with a formatted byte array.
29         /// </summary>
30         /// <param name="data">The formatted byte array</param>
31         /// <param name="recno">
32         /// Whether it is buffer for keys in <see cref="RecnoDatabase"/> or 
33         /// <see cref="QueueDatabase"/>
34         /// </param>
35         public MultipleDatabaseEntry(byte[] data, bool recno) {
36             UserData = data;
37             _data = UserData;
38             _recno = recno;
39             flags |= DbConstants.DB_DBT_BULK;
40         }
41
42         /// <summary>
43         /// Construct a new key or data bulk buffer with a bunch of record 
44         /// numbers, as keys in <see cref="RecnoDatabase"/> or
45         /// <see cref="QueueDatabase"/>
46         /// </summary>
47         /// <param name="recnos">Enumerable record numbers</param>
48         public MultipleDatabaseEntry(IEnumerable<uint> recnos) {
49             List<byte> list = new List<byte>();
50             byte[] arr;
51             byte[] lenArr = BitConverter.GetBytes(4);
52             byte tmp;
53             int i, j;
54             uint pos = 0;
55
56             /* 
57              * The first record number is written at the end of the array, 
58              * preceded by its offset in the array and the length of the record
59              * number (the length is always 4 bytes.)  Each entry in the array
60              * (record number, offset & length) is 12 bytes.
61              *
62              * 0 is an invalid record number and is used to mark the end of the
63              * array of record numbers.  Since records are written from the
64              * back of the array, the first 4 bytes of the array are zeros.
65              * -----------------------------------------------------
66              * |0|lengthN|offsetN|recnoN|...|length1|offset1|recno1|
67              * -----------------------------------------------------
68              */
69             foreach (uint recno in recnos) {
70                 arr = BitConverter.GetBytes(recno);
71                 for (i = 0; i < 4; i++)
72                     list.Add(arr[i]);
73                 arr = BitConverter.GetBytes(pos);
74                 for (i = 0; i < 4; i++)
75                     list.Add(arr[i]);
76                 for (i = 0; i < 4; i++)
77                     list.Add(lenArr[i]);
78                 pos += 12;
79             }
80             arr = BitConverter.GetBytes(0);
81             for (i = 0; i < 4; i++)
82                 list.Add(arr[i]);
83             for (i = 0; i < list.Count / (2 * 4); i++) {
84                 for (j = 0; j < 4; j++) {
85                     tmp = list[i * 4 + j];
86                     list[i * 4 + j] = list[list.Count - 4 + j - i * 4];
87                     list[list.Count - 4 + j - i * 4] = tmp;
88                 }
89             }
90             UserData = list.ToArray();
91             _data = UserData;
92             _recno = true;
93             flags |= DbConstants.DB_DBT_BULK;
94         }
95
96         /// <summary>
97         /// Construct a new key or data bulk buffer with a bunch of 
98         /// <see cref="DatabaseEntry"/> referring to <paramref name="recno"/>.
99         /// </summary>
100         /// <param name="data">Enumerable data</param>
101         /// <param name="recno">
102         /// Whether it is buffer for keys in <see cref="RecnoDatabase"/>
103         /// or <see cref="QueueDatabase"/>
104         /// </param>
105         public MultipleDatabaseEntry(
106             IEnumerable<DatabaseEntry> data, bool recno) {
107             List<byte> list = new List<byte>();
108             List<uint> metadata = new List<uint>();
109             byte[] arr;
110             byte[] lenArr = BitConverter.GetBytes(4);
111             int i, j;
112             uint pos = 0;
113             uint size;
114             byte tmp;
115
116             if (recno) {
117                 /*
118                  * For recno DatabaseEntry, everything is stored in the header,
119                  * as described in the comment above.  The layout, again, is:
120                  * -----------------------------------------------------
121                  * |0|lengthN|offsetN|recnoN|...|length1|offset1|recno1|
122                  * -----------------------------------------------------
123                  */
124                 foreach (DatabaseEntry dbtObj in data) {
125                     arr = dbtObj.Data;
126                     for (i = 0; i < 4; i++)
127                         list.Add(arr[i]);
128                     arr = BitConverter.GetBytes(pos);
129                     for (i = 0; i < 4; i++)
130                         list.Add(arr[i]);
131                     for (i = 0; i < 4; i++)
132                         list.Add(lenArr[i]);
133                     pos += 12;
134                 }
135                 arr = BitConverter.GetBytes(0);
136                 for (i = 0; i < 4; i++)
137                     list.Add(arr[i]);
138                 for (i = 0; i < list.Count / (2 * 4); i++) {
139                     for (j = 0; j < 4; j++) {
140                         tmp = list[i * 4 + j];
141                         list[i * 4 + j] = list[list.Count - 4 + j - i * 4];
142                         list[list.Count - 4 + j - i * 4] = tmp;
143                     }
144                 }
145             } else {
146                 /* 
147                  * For non-recno DatabaseEntry, the data values are written
148                  * sequentially to the start of the array.  Header information
149                  * is written at the end of the array.  Header information is
150                  * the offset in the array at which the data value starts and
151                  * the length of the data (each header entry occupies 8 bytes.)
152                  * The data values are separated from the header information by
153                  * a value of -1.  The layout is:
154                  * ---------------------------------------------------------
155                  * |data1|...|dataN||-1|lengthN|offsetN|...|length1|offset1|
156                  * ---------------------------------------------------------
157                  */
158                 foreach (DatabaseEntry dbtObj in data) {
159                     size = dbtObj.size;
160                     arr = dbtObj.Data;
161                     metadata.Add(pos);
162                     metadata.Add(size);
163                     for (i = 0; i < (int)size; i++)
164                         list.Add(arr[i]);
165                     pos += size;
166                 }
167                 arr = BitConverter.GetBytes(-1);
168                 for (i = 0; i < 4; i++)
169                     list.Add(arr[i]);
170                 for (i = metadata.Count; i > 0; i--) {
171                     arr = BitConverter.GetBytes(metadata[i - 1]);
172                     for (j = 0; j < 4; j++)
173                         list.Add(arr[j]);
174                 }
175             }
176             UserData = list.ToArray();
177             _data = UserData;
178             _recno = recno;
179             flags |= DbConstants.DB_DBT_BULK;
180         }
181
182         /// <summary>
183         /// Return the number of records in the DBT.
184         /// </summary>
185         internal int nRecs {
186             get {
187                 uint pos = ulen - 4;
188                 int ret = 0;
189                 if (_recno) {
190                     int recno;
191                     while ((recno = BitConverter.ToInt32(_data, (int)pos)) != 0) {
192                         ret++;
193                         pos -= 12;
194                     }
195                 } else {
196                     int off;
197                     while ((off = BitConverter.ToInt32(_data, (int)pos)) >= 0) {
198                         ret++;
199                         pos -= 8;
200                     }
201                 }
202                 return ret;
203             }
204         }
205
206         /// <summary>
207         /// Whether the bulk buffer is for recno or queue database.
208         /// </summary>
209         public bool Recno {
210             get { return _recno; }
211         }
212
213         IEnumerator IEnumerable.GetEnumerator() {
214             return GetEnumerator();
215         }
216
217         /// <summary>
218         /// Return an enumerator which iterates over all
219         /// <see cref="DatabaseEntry"/> objects represented by the 
220         /// <see cref="MultipleDatabaseEntry"/>.
221         /// </summary>
222         /// <returns>
223         /// An enumerator for the <see cref="MultipleDatabaseEntry"/>
224         /// </returns>
225         public IEnumerator<DatabaseEntry> GetEnumerator() {
226             uint pos;
227             if (_recno) {
228                 int recno;
229                 pos = ulen - 4;
230                 while ((recno = BitConverter.ToInt32(_data, (int)pos)) != 0) {
231                     byte[] arr = new byte[4];
232                     Array.Copy(_data, pos, arr, 0, 4);
233                     pos -= 12;
234                     yield return new DatabaseEntry(arr);
235                 }
236             } else {
237                 pos = ulen - 4;
238                 int off = BitConverter.ToInt32(_data, (int)pos);
239                 for (int i = 0; off >= 0;
240                     off = BitConverter.ToInt32(_data, (int)pos), i++) {
241                     pos -= 4;
242                     int sz = BitConverter.ToInt32(_data, (int)pos);
243                     byte[] arr = new byte[sz];
244                     Array.Copy(_data, off, arr, 0, sz);
245                     pos -= 4;
246                     yield return new DatabaseEntry(arr);
247                 }
248             }
249         }
250         
251         // public byte[][] Data;
252         /* No Public Constructor */
253         //internal MultipleDatabaseEntry(DatabaseEntry dbt) {
254         //    byte[] dat = dbt.UserData;
255         //    List<byte[]> tmp = new List<byte[]>();
256         //    uint pos = dbt.ulen - 4;
257         //    int off = BitConverter.ToInt32(dat, (int)pos);
258         //    for (int i = 0; off > 0; off = BitConverter.ToInt32(dat, (int)pos), i++) {
259         //        pos -= 4;
260         //        int sz = BitConverter.ToInt32(dat, (int)pos);
261         //        tmp.Add(new byte[sz]);
262         //        Array.Copy(dat, off, tmp[i], 0, sz);
263         //        pos -= 4;
264         //    }
265         //    Data = tmp.ToArray();
266         //}
267     }
268 }