1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2009 Oracle.  All rights reserved.
5 *
6 */
7using System;
8using System.Collections.Generic;
9using System.Text;
10using BerkeleyDB.Internal;
11
12namespace BerkeleyDB {
13    /// <summary>
14    /// A class representing a RecnoDatabase. The Recno format supports fixed-
15    /// or variable-length records, accessed sequentially or by logical record
16    /// number, and optionally backed by a flat text file.
17    /// </summary>
18    public class RecnoDatabase : Database {
19        private BDB_AppendRecnoDelegate doAppendRef;
20        private AppendRecordDelegate appendHandler;
21
22        #region Constructors
23        private RecnoDatabase(DatabaseEnvironment env, uint flags)
24            : base(env, flags) { }
25        internal RecnoDatabase(BaseDatabase clone) : base(clone) { }
26
27        private void Config(RecnoDatabaseConfig cfg) {
28            base.Config(cfg);
29            /*
30             * Database.Config calls set_flags, but that doesn't get the Recno
31             * specific flags.  No harm in calling it again.
32             */
33            db.set_flags(cfg.flags);
34
35            if (cfg.delimiterIsSet)
36                RecordDelimiter = cfg.Delimiter;
37            if (cfg.lengthIsSet)
38                RecordLength = cfg.Length;
39            if (cfg.padIsSet)
40                RecordPad = cfg.PadByte;
41            if (cfg.BackingFile != null)
42                SourceFile = cfg.BackingFile;
43        }
44
45        /// <summary>
46        /// Instantiate a new RecnoDatabase object and open the database
47        /// represented by <paramref name="Filename"/>.
48        /// </summary>
49        /// <remarks>
50        /// <para>
51        /// If <paramref name="Filename"/> is null, the database is strictly
52        /// temporary and cannot be opened by any other thread of control, thus
53        /// the database can only be accessed by sharing the single database
54        /// object that created it, in circumstances where doing so is safe.
55        /// </para>
56        /// <para>
57        /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
58        /// will be implicitly transaction protected. Note that transactionally
59        /// protected operations on a datbase object requires the object itself
60        /// be transactionally protected during its open.
61        /// </para>
62        /// </remarks>
63        /// <param name="Filename">
64        /// The name of an underlying file that will be used to back the
65        /// database. In-memory databases never intended to be preserved on disk
66        /// may be created by setting this parameter to null.
67        /// </param>
68        /// <param name="cfg">The database's configuration</param>
69        /// <returns>A new, open database object</returns>
70        public static RecnoDatabase Open(
71            string Filename, RecnoDatabaseConfig cfg) {
72            return Open(Filename, null, cfg, null);
73        }
74        /// <summary>
75        /// Instantiate a new RecnoDatabase object and open the database
76        /// represented by <paramref name="Filename"/> and
77        /// <paramref name="DatabaseName"/>.
78        /// </summary>
79        /// <remarks>
80        /// <para>
81        /// If both <paramref name="Filename"/> and
82        /// <paramref name="DatabaseName"/> are null, the database is strictly
83        /// temporary and cannot be opened by any other thread of control, thus
84        /// the database can only be accessed by sharing the single database
85        /// object that created it, in circumstances where doing so is safe. If
86        /// <paramref name="Filename"/> is null and
87        /// <paramref name="DatabaseName"/> is non-null, the database can be
88        /// opened by other threads of control and will be replicated to client
89        /// sites in any replication group.
90        /// </para>
91        /// <para>
92        /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
93        /// will be implicitly transaction protected. Note that transactionally
94        /// protected operations on a datbase object requires the object itself
95        /// be transactionally protected during its open.
96        /// </para>
97        /// </remarks>
98        /// <param name="Filename">
99        /// The name of an underlying file that will be used to back the
100        /// database. In-memory databases never intended to be preserved on disk
101        /// may be created by setting this parameter to null.
102        /// </param>
103        /// <param name="DatabaseName">
104        /// This parameter allows applications to have multiple databases in a
105        /// single file. Although no DatabaseName needs to be specified, it is
106        /// an error to attempt to open a second database in a file that was not
107        /// initially created using a database name.
108        /// </param>
109        /// <param name="cfg">The database's configuration</param>
110        /// <returns>A new, open database object</returns>
111        public static RecnoDatabase Open(
112            string Filename, string DatabaseName, RecnoDatabaseConfig cfg) {
113            return Open(Filename, DatabaseName, cfg, null);
114        }
115        /// <summary>
116        /// Instantiate a new RecnoDatabase object and open the database
117        /// represented by <paramref name="Filename"/>.
118        /// </summary>
119        /// <remarks>
120        /// <para>
121        /// If <paramref name="Filename"/> is null, the database is strictly
122        /// temporary and cannot be opened by any other thread of control, thus
123        /// the database can only be accessed by sharing the single database
124        /// object that created it, in circumstances where doing so is safe.
125        /// </para>
126        /// <para>
127        /// If <paramref name="txn"/> is null, but
128        /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
129        /// be implicitly transaction protected. Note that transactionally
130        /// protected operations on a datbase object requires the object itself
131        /// be transactionally protected during its open. Also note that the
132        /// transaction must be committed before the object is closed.
133        /// </para>
134        /// </remarks>
135        /// <param name="Filename">
136        /// The name of an underlying file that will be used to back the
137        /// database. In-memory databases never intended to be preserved on disk
138        /// may be created by setting this parameter to null.
139        /// </param>
140        /// <param name="cfg">The database's configuration</param>
141        /// <param name="txn">
142        /// If the operation is part of an application-specified transaction,
143        /// <paramref name="txn"/> is a Transaction object returned from
144        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
145        /// the operation is part of a Berkeley DB Concurrent Data Store group,
146        /// <paramref name="txn"/> is a handle returned from
147        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
148        /// </param>
149        /// <returns>A new, open database object</returns>
150        public static RecnoDatabase Open(
151            string Filename, RecnoDatabaseConfig cfg, Transaction txn) {
152            return Open(Filename, null, cfg, txn);
153        }
154        /// <summary>
155        /// Instantiate a new RecnoDatabase object and open the database
156        /// represented by <paramref name="Filename"/> and
157        /// <paramref name="DatabaseName"/>.
158        /// </summary>
159        /// <remarks>
160        /// <para>
161        /// If both <paramref name="Filename"/> and
162        /// <paramref name="DatabaseName"/> are null, the database is strictly
163        /// temporary and cannot be opened by any other thread of control, thus
164        /// the database can only be accessed by sharing the single database
165        /// object that created it, in circumstances where doing so is safe. If
166        /// <paramref name="Filename"/> is null and
167        /// <paramref name="DatabaseName"/> is non-null, the database can be
168        /// opened by other threads of control and will be replicated to client
169        /// sites in any replication group.
170        /// </para>
171        /// <para>
172        /// If <paramref name="txn"/> is null, but
173        /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
174        /// be implicitly transaction protected. Note that transactionally
175        /// protected operations on a datbase object requires the object itself
176        /// be transactionally protected during its open. Also note that the
177        /// transaction must be committed before the object is closed.
178        /// </para>
179        /// </remarks>
180        /// <param name="Filename">
181        /// The name of an underlying file that will be used to back the
182        /// database. In-memory databases never intended to be preserved on disk
183        /// may be created by setting this parameter to null.
184        /// </param>
185        /// <param name="DatabaseName">
186        /// This parameter allows applications to have multiple databases in a
187        /// single file. Although no DatabaseName needs to be specified, it is
188        /// an error to attempt to open a second database in a file that was not
189        /// initially created using a database name.
190        /// </param>
191        /// <param name="cfg">The database's configuration</param>
192        /// <param name="txn">
193        /// If the operation is part of an application-specified transaction,
194        /// <paramref name="txn"/> is a Transaction object returned from
195        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
196        /// the operation is part of a Berkeley DB Concurrent Data Store group,
197        /// <paramref name="txn"/> is a handle returned from
198        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
199        /// </param>
200        /// <returns>A new, open database object</returns>
201        public static RecnoDatabase Open(string Filename,
202            string DatabaseName, RecnoDatabaseConfig cfg, Transaction txn) {
203            RecnoDatabase ret = new RecnoDatabase(cfg.Env, 0);
204            ret.Config(cfg);
205            ret.db.open(Transaction.getDB_TXN(txn),
206                Filename, DatabaseName, DBTYPE.DB_RECNO, cfg.openFlags, 0);
207            ret.isOpen = true;
208            return ret;
209        }
210        #endregion Constructors
211
212        #region Callbacks
213        private static void doAppend(IntPtr dbp, IntPtr dbtp1, uint recno) {
214            DB db = new DB(dbp, false);
215            DBT dbt1 = new DBT(dbtp1, false);
216            RecnoDatabase rdb = (RecnoDatabase)(db.api_internal);
217
218            rdb.AppendCallback(DatabaseEntry.fromDBT(dbt1), recno);
219        }
220        #endregion Callbacks
221
222        #region Properties
223        /// <summary>
224        /// A function to call after the record number has been selected but
225        /// before the data has been stored into the database.
226        /// </summary>
227        /// <remarks>
228        /// <para>
229        /// When using <see cref="QueueDatabase.Append"/>, it may be useful to
230        /// modify the stored data based on the generated key. If a delegate is
231        /// specified, it will be called after the record number has been
232        /// selected, but before the data has been stored.
233        /// </para>
234        /// </remarks>
235        public AppendRecordDelegate AppendCallback {
236            get { return appendHandler; }
237            set {
238                if (value == null)
239                    db.set_append_recno(null);
240                else if (appendHandler == null) {
241                    if (doAppendRef == null)
242                        doAppendRef = new BDB_AppendRecnoDelegate(doAppend);
243                    db.set_append_recno(doAppendRef);
244                }
245                appendHandler = value;
246            }
247        }
248
249        /// <summary>
250        /// The delimiting byte used to mark the end of a record in
251        /// <see cref="SourceFile"/>.
252        /// </summary>
253        public int RecordDelimiter {
254            get {
255                int ret = 0;
256                db.get_re_delim(ref ret);
257                return ret;
258            }
259            private set {
260                db.set_re_delim(value);
261            }
262        }
263
264        /// <summary>
265        /// If using fixed-length, not byte-delimited records, the length of the
266        /// records.
267        /// </summary>
268        public uint RecordLength {
269            get {
270                uint ret = 0;
271                db.get_re_len(ref ret);
272                return ret;
273            }
274            private set {
275                db.set_re_len(value);
276            }
277        }
278
279        /// <summary>
280        /// The padding character for short, fixed-length records.
281        /// </summary>
282        public int RecordPad {
283            get {
284                int ret = 0;
285                db.get_re_pad(ref ret);
286                return ret;
287            }
288            private set {
289                db.set_re_pad(value);
290            }
291        }
292
293        /// <summary>
294        /// If true, the logical record numbers are mutable, and change as
295        /// records are added to and deleted from the database.
296        /// </summary>
297        public bool Renumber {
298            get {
299                uint flags = 0;
300                db.get_flags(ref flags);
301                return (flags & DbConstants.DB_RENUMBER) != 0;
302            }
303        }
304
305        /// <summary>
306        /// If true, any <see cref="SourceFile"/> file will be read in its
307        /// entirety when <see cref="Open"/> is called. If false,
308        /// <see cref="SourceFile"/> may be read lazily.
309        /// </summary>
310        public bool Snapshot {
311            get {
312                uint flags = 0;
313                db.get_flags(ref flags);
314                return (flags & DbConstants.DB_SNAPSHOT) != 0;
315            }
316        }
317
318        /// <summary>
319        /// The underlying source file for the Recno access method.
320        /// </summary>
321        public string SourceFile {
322            get {
323                string ret = "";
324                db.get_re_source(ref ret);
325                return ret;
326            }
327            private set {
328                db.set_re_source(value);
329            }
330        }
331
332        #endregion Properties
333
334        #region Methods
335        /// <summary>
336        /// Append the data item to the end of the database.
337        /// </summary>
338        /// <param name="data">The data item to store in the database</param>
339        /// <returns>The record number allocated to the record</returns>
340        public uint Append(DatabaseEntry data) {
341            return Append(data, null);
342        }
343        /// <summary>
344        /// Append the data item to the end of the database.
345        /// </summary>
346        /// <remarks>
347        /// There is a minor behavioral difference between
348        /// <see cref="RecnoDatabase.Append"/> and
349        /// <see cref="QueueDatabase.Append"/>. If a transaction enclosing an
350        /// Append operation aborts, the record number may be reallocated in a
351        /// subsequent <see cref="RecnoDatabase.Append"/> operation, but it will
352        /// not be reallocated in a subsequent
353        /// <see cref="QueueDatabase.Append"/> operation.
354        /// </remarks>
355        /// <param name="data">The data item to store in the database</param>
356        /// <param name="txn">
357        /// If the operation is part of an application-specified transaction,
358        /// <paramref name="txn"/> is a Transaction object returned from
359        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
360        /// the operation is part of a Berkeley DB Concurrent Data Store group,
361        /// <paramref name="txn"/> is a handle returned from
362        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
363        /// </param>
364        /// <returns>The record number allocated to the record</returns>
365        public uint Append(DatabaseEntry data, Transaction txn) {
366            DatabaseEntry key = new DatabaseEntry();
367            Put(key, data, txn, DbConstants.DB_APPEND);
368            return BitConverter.ToUInt32(key.Data, 0);
369        }
370
371        /// <summary>
372        /// Compact the database, and optionally return unused database pages to
373        /// the underlying filesystem.
374        /// </summary>
375        /// <remarks>
376        /// If the operation occurs in a transactional database, the operation
377        /// will be implicitly transaction protected using multiple
378        /// transactions. These transactions will be periodically committed to
379        /// avoid locking large sections of the tree. Any deadlocks encountered
380        /// cause the compaction operation to be retried from the point of the
381        /// last transaction commit.
382        /// </remarks>
383        /// <param name="cdata">Compact configuration parameters</param>
384        /// <returns>Compact operation statistics</returns>
385        public CompactData Compact(CompactConfig cdata) {
386            return Compact(cdata, null);
387        }
388        /// <summary>
389        /// Compact the database, and optionally return unused database pages to
390        /// the underlying filesystem.
391        /// </summary>
392        /// <remarks>
393        /// <para>
394        /// If <paramref name="txn"/> is non-null, then the operation is
395        /// performed using that transaction. In this event, large sections of
396        /// the tree may be locked during the course of the transaction.
397        /// </para>
398        /// <para>
399        /// If <paramref name="txn"/> is null, but the operation occurs in a
400        /// transactional database, the operation will be implicitly transaction
401        /// protected using multiple transactions. These transactions will be
402        /// periodically committed to avoid locking large sections of the tree.
403        /// Any deadlocks encountered cause the compaction operation to be
404        /// retried from the point of the last transaction commit.
405        /// </para>
406        /// </remarks>
407        /// <param name="cdata">Compact configuration parameters</param>
408        /// <param name="txn">
409        /// If the operation is part of an application-specified transaction,
410        /// <paramref name="txn"/> is a Transaction object returned from
411        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
412        /// the operation is part of a Berkeley DB Concurrent Data Store group,
413        /// <paramref name="txn"/> is a handle returned from
414        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
415        /// </param>
416        /// <returns>Compact operation statistics</returns>
417        public CompactData Compact(CompactConfig cdata, Transaction txn) {
418            DatabaseEntry end = null;
419            if (cdata.returnEnd)
420                end = new DatabaseEntry();
421
422            db.compact(Transaction.getDB_TXN(txn),
423                cdata.start,
424                cdata.stop,
425                CompactConfig.getDB_COMPACT(cdata),
426                cdata.flags, end);
427            return new CompactData(CompactConfig.getDB_COMPACT(cdata), end);
428        }
429
430        /// <summary>
431        /// Create a database cursor.
432        /// </summary>
433        /// <returns>A newly created cursor</returns>
434        public new RecnoCursor Cursor() {
435            return Cursor(new CursorConfig(), null);
436        }
437        /// <summary>
438        /// Create a database cursor with the given configuration.
439        /// </summary>
440        /// <param name="cfg">
441        /// The configuration properties for the cursor.
442        /// </param>
443        /// <returns>A newly created cursor</returns>
444        public new RecnoCursor Cursor(CursorConfig cfg) {
445            return Cursor(cfg, null);
446        }
447        /// <summary>
448        /// Create a transactionally protected database cursor.
449        /// </summary>
450        /// <param name="txn">
451        /// The transaction context in which the cursor may be used.
452        /// </param>
453        /// <returns>A newly created cursor</returns>
454        public new RecnoCursor Cursor(Transaction txn) {
455            return Cursor(new CursorConfig(), txn);
456        }
457        /// <summary>
458        /// Create a transactionally protected database cursor with the given
459        /// configuration.
460        /// </summary>
461        /// <param name="cfg">
462        /// The configuration properties for the cursor.
463        /// </param>
464        /// <param name="txn">
465        /// The transaction context in which the cursor may be used.
466        /// </param>
467        /// <returns>A newly created cursor</returns>
468        public new RecnoCursor Cursor(CursorConfig cfg, Transaction txn) {
469            return new RecnoCursor(
470                db.cursor(Transaction.getDB_TXN(txn), cfg.flags), Pagesize);
471        }
472
473        /// <summary>
474        /// Return the database statistical information which does not require
475        /// traversal of the database.
476        /// </summary>
477        /// <returns>
478        /// The database statistical information which does not require
479        /// traversal of the database.
480        /// </returns>
481        public RecnoStats FastStats() {
482            return Stats(null, true, Isolation.DEGREE_THREE);
483        }
484        /// <summary>
485        /// Return the database statistical information which does not require
486        /// traversal of the database.
487        /// </summary>
488        /// <param name="txn">
489        /// If the operation is part of an application-specified transaction,
490        /// <paramref name="txn"/> is a Transaction object returned from
491        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
492        /// the operation is part of a Berkeley DB Concurrent Data Store group,
493        /// <paramref name="txn"/> is a handle returned from
494        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
495        /// </param>
496        /// <returns>
497        /// The database statistical information which does not require
498        /// traversal of the database.
499        /// </returns>
500        public RecnoStats FastStats(Transaction txn) {
501            return Stats(txn, true, Isolation.DEGREE_THREE);
502        }
503        /// <summary>
504        /// Return the database statistical information which does not require
505        /// traversal of the database.
506        /// </summary>
507        /// <overloads>
508        /// <para>
509        /// Among other things, this method makes it possible for applications
510        /// to request key and record counts without incurring the performance
511        /// penalty of traversing the entire database.
512        /// </para>
513        /// <para>
514        /// The statistical information is described by the
515        /// <see cref="BTreeStats"/>, <see cref="HashStats"/>,
516        /// <see cref="QueueStats"/>, and <see cref="RecnoStats"/> classes.
517        /// </para>
518        /// </overloads>
519        /// <param name="txn">
520        /// If the operation is part of an application-specified transaction,
521        /// <paramref name="txn"/> is a Transaction object returned from
522        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
523        /// the operation is part of a Berkeley DB Concurrent Data Store group,
524        /// <paramref name="txn"/> is a handle returned from
525        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
526        /// </param>
527        /// <param name="isoDegree">
528        /// The level of isolation for database reads.
529        /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
530        /// databases which did not specify
531        /// <see cref="DatabaseConfig.ReadUncommitted"/>.
532        /// </param>
533        /// <returns>
534        /// The database statistical information which does not require
535        /// traversal of the database.
536        /// </returns>
537        public RecnoStats FastStats(Transaction txn, Isolation isoDegree) {
538            return Stats(txn, true, isoDegree);
539        }
540
541        /// <summary>
542        /// Return the database statistical information for this database.
543        /// </summary>
544        /// <returns>Database statistical information.</returns>
545        public RecnoStats Stats() {
546            return Stats(null, false, Isolation.DEGREE_THREE);
547        }
548        /// <summary>
549        /// Return the database statistical information for this database.
550        /// </summary>
551        /// <param name="txn">
552        /// If the operation is part of an application-specified transaction,
553        /// <paramref name="txn"/> is a Transaction object returned from
554        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
555        /// the operation is part of a Berkeley DB Concurrent Data Store group,
556        /// <paramref name="txn"/> is a handle returned from
557        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
558        /// </param>
559        /// <returns>Database statistical information.</returns>
560        public RecnoStats Stats(Transaction txn) {
561            return Stats(txn, false, Isolation.DEGREE_THREE);
562        }
563        /// <summary>
564        /// Return the database statistical information for this database.
565        /// </summary>
566        /// <overloads>
567        /// The statistical information is described by
568        /// <see cref="BTreeStats"/>.
569        /// </overloads>
570        /// <param name="txn">
571        /// If the operation is part of an application-specified transaction,
572        /// <paramref name="txn"/> is a Transaction object returned from
573        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
574        /// the operation is part of a Berkeley DB Concurrent Data Store group,
575        /// <paramref name="txn"/> is a handle returned from
576        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
577        /// </param>
578        /// <param name="isoDegree">
579        /// The level of isolation for database reads.
580        /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
581        /// databases which did not specify
582        /// <see cref="DatabaseConfig.ReadUncommitted"/>.
583        /// </param>
584        /// <returns>Database statistical information.</returns>
585        public RecnoStats Stats(Transaction txn, Isolation isoDegree) {
586            return Stats(txn, false, isoDegree);
587        }
588        private RecnoStats Stats(
589            Transaction txn, bool fast, Isolation isoDegree) {
590            uint flags = 0;
591            flags |= fast ? DbConstants.DB_FAST_STAT : 0;
592            switch (isoDegree) {
593                case Isolation.DEGREE_ONE:
594                    flags |= DbConstants.DB_READ_UNCOMMITTED;
595                    break;
596                case Isolation.DEGREE_TWO:
597                    flags |= DbConstants.DB_READ_COMMITTED;
598                    break;
599            }
600            BTreeStatStruct st = db.stat_bt(Transaction.getDB_TXN(txn), flags);
601            return new RecnoStats(st);
602        }
603
604        /// <summary>
605        /// Return pages to the filesystem that are already free and at the end
606        /// of the file.
607        /// </summary>
608        /// <returns>
609        /// The number of database pages returned to the filesystem
610        /// </returns>
611        public uint TruncateUnusedPages() {
612            return TruncateUnusedPages(null);
613        }
614        /// <summary>
615        /// Return pages to the filesystem that are already free and at the end
616        /// of the file.
617        /// </summary>
618        /// <param name="txn">
619        /// If the operation is part of an application-specified transaction,
620        /// <paramref name="txn"/> is a Transaction object returned from
621        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
622        /// the operation is part of a Berkeley DB Concurrent Data Store group,
623        /// <paramref name="txn"/> is a handle returned from
624        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
625        /// </param>
626        /// <returns>
627        /// The number of database pages returned to the filesystem
628        /// </returns>
629        public uint TruncateUnusedPages(Transaction txn) {
630            DB_COMPACT cdata = new DB_COMPACT();
631            db.compact(Transaction.getDB_TXN(txn),
632                null, null, cdata, DbConstants.DB_FREELIST_ONLY, null);
633            return cdata.compact_pages_truncated;
634        }
635        #endregion Methods
636    }
637}