1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997,2008 Oracle.  All rights reserved.
5 *
6 * $Id: TpcbExample.java,v 12.11 2008/01/08 20:58:32 bostic Exp $
7 */
8
9package db;
10
11import com.sleepycat.db.*;
12
13import java.io.File;
14import java.io.FileNotFoundException;
15import java.math.BigDecimal;
16import java.util.Calendar;
17import java.util.Date;
18import java.util.Random;
19import java.util.GregorianCalendar;
20
21//
22// This program implements a basic TPC/B driver program.  To create the
23// TPC/B database, run with the -i (init) flag.  The number of records
24// with which to populate the account, history, branch, and teller tables
25// is specified by the a, s, b, and t flags respectively.  To run a TPC/B
26// test, use the n flag to indicate a number of transactions to run in
27// each thread and -T to specify the number of threads.
28//
29class TpcbExample {
30    public static final int TELLERS_PER_BRANCH = 10;
31    public static final int ACCOUNTS_PER_TELLER = 10000;
32    public static final int HISTORY_PER_BRANCH = 2592000;
33
34    //
35    // The default configuration that adheres to TPCB scaling rules requires
36    // nearly 3 GB of space.  To avoid requiring that much space for testing,
37    // we set the parameters much lower.  If you want to run a valid 10 TPS
38    // configuration, uncomment the VALID_SCALING configuration
39    //
40
41    // VALID_SCALING configuration
42    /*
43      public static final int ACCOUNTS = 1000000;
44      public static final int BRANCHES = 10;
45      public static final int TELLERS = 100;
46      public static final int HISTORY = 25920000;
47    */
48
49    // TINY configuration
50    /*
51      public static final int ACCOUNTS = 1000;
52      public static final int BRANCHES = 10;
53      public static final int TELLERS = 100;
54      public static final int HISTORY = 10000;
55    */
56
57    // Default configuration
58    public static final int ACCOUNTS = 100000;
59    public static final int BRANCHES = 10;
60    public static final int TELLERS = 100;
61    public static final int HISTORY = 259200;
62
63    public static final int HISTORY_LEN = 100;
64    public static final int RECLEN = 100;
65    public static final int BEGID = 1000000;
66
67    // used by random_id()
68    public static final int ACCOUNT = 0;
69    public static final int BRANCH = 1;
70    public static final int TELLER = 2;
71
72    public static boolean verbose = false;
73    public static final String progname = "TpcbExample";    // Program name.
74
75    Environment dbenv;
76    int accounts, branches, tellers, history;
77
78    public TpcbExample(File home,
79                       int accounts, int branches, int tellers, int history,
80                       int cachesize, boolean noSync)
81        throws DatabaseException, FileNotFoundException {
82
83        this.accounts = accounts;
84        this.branches = branches;
85        this.tellers = tellers;
86        this.history = history;
87
88        EnvironmentConfig config = new EnvironmentConfig();
89        config.setErrorStream(System.err);
90        config.setErrorPrefix(progname);
91        config.setCacheSize(cachesize == 0 ? 4 * 1024 * 1024 : cachesize);
92        config.setTxnNoSync(noSync);
93        config.setLockDetectMode(LockDetectMode.DEFAULT);
94        config.setAllowCreate(true);
95
96        config.setInitializeCache(true);
97        config.setTransactional(true);
98        config.setInitializeLocking(true);
99        config.setInitializeLogging(true);
100
101        dbenv = new Environment(home, config);
102    }
103
104    public void close()
105        throws DatabaseException {
106
107        try {
108            if (dbenv != null)
109                dbenv.close();
110        } finally {
111            dbenv = null;
112        }
113    }
114
115    //
116    // Initialize the database to the number of accounts, branches,
117    // history records, and tellers given to the constructor.
118    //
119    public void populate() {
120        Database dbp = null;
121
122        int err;
123        int balance, idnum;
124        int end_anum, end_bnum, end_tnum;
125        int start_anum, start_bnum, start_tnum;
126        int h_nelem;
127
128        idnum = BEGID;
129        balance = 500000;
130
131        h_nelem = accounts;
132
133        try {
134            DatabaseConfig config = new DatabaseConfig();
135            config.setType(DatabaseType.HASH);
136            config.setHashNumElements(h_nelem);
137            config.setAllowCreate(true);
138            dbp = dbenv.openDatabase(null, "account", null, config);
139        } catch (Exception e1) {
140            // can be DatabaseException or FileNotFoundException
141            errExit(e1, "Open of account file failed");
142        }
143
144        start_anum = idnum;
145        populateTable(dbp, idnum, balance, h_nelem, "account");
146        idnum += h_nelem;
147        end_anum = idnum - 1;
148        try {
149            dbp.close();
150        } catch (DatabaseException e2) {
151            errExit(e2, "Account file close failed");
152        }
153
154        if (verbose)
155            System.out.println("Populated accounts: " +
156                               String.valueOf(start_anum) + " - " +
157                               String.valueOf(end_anum));
158
159        //
160        // Since the number of branches is very small, we want to use very
161        // small pages and only 1 key per page.  This is the poor-man's way
162        // of getting key locking instead of page locking.
163        //
164        h_nelem = (int)branches;
165
166        try {
167            DatabaseConfig config = new DatabaseConfig();
168            config.setType(DatabaseType.HASH);
169            config.setHashNumElements(h_nelem);
170            config.setHashFillFactor(1);
171            config.setPageSize(512);
172            config.setAllowCreate(true);
173            dbp = dbenv.openDatabase(null, "branch", null, config);
174        } catch (Exception e3) {
175            // can be DatabaseException or FileNotFoundException
176            errExit(e3, "Branch file create failed");
177        }
178
179        start_bnum = idnum;
180        populateTable(dbp, idnum, balance, h_nelem, "branch");
181        idnum += h_nelem;
182        end_bnum = idnum - 1;
183
184        try {
185            dbp.close();
186        } catch (DatabaseException dbe4) {
187            errExit(dbe4, "Close of branch file failed");
188        }
189
190        if (verbose)
191            System.out.println("Populated branches: " +
192                               String.valueOf(start_bnum) + " - " +
193                               String.valueOf(end_bnum));
194
195        //
196        // In the case of tellers, we also want small pages, but we'll let
197        // the fill factor dynamically adjust itself.
198        //
199        h_nelem = (int)tellers;
200
201        try {
202            DatabaseConfig config = new DatabaseConfig();
203            config.setType(DatabaseType.HASH);
204            config.setHashNumElements(h_nelem);
205            config.setHashFillFactor(0);
206            config.setPageSize(512);
207            config.setAllowCreate(true);
208            dbp = dbenv.openDatabase(null, "teller", null, config);
209        } catch (Exception e5) {
210            // can be DatabaseException or FileNotFoundException
211            errExit(e5, "Teller file create failed");
212        }
213
214        start_tnum = idnum;
215        populateTable(dbp, idnum, balance, h_nelem, "teller");
216        idnum += h_nelem;
217        end_tnum = idnum - 1;
218
219        try {
220            dbp.close();
221        } catch (DatabaseException e6) {
222            errExit(e6, "Close of teller file failed");
223        }
224
225        if (verbose)
226            System.out.println("Populated tellers: " +
227                               String.valueOf(start_tnum) + " - " +
228                               String.valueOf(end_tnum));
229
230        try {
231            DatabaseConfig config = new DatabaseConfig();
232            config.setType(DatabaseType.RECNO);
233            config.setRecordLength(HISTORY_LEN);
234            config.setAllowCreate(true);
235            dbp = dbenv.openDatabase(null, "history", null, config);
236        } catch (Exception e7) {
237            // can be DatabaseException or FileNotFoundException
238            errExit(e7, "Create of history file failed");
239        }
240
241        populateHistory(dbp);
242
243        try {
244            dbp.close();
245        } catch (DatabaseException e8) {
246            errExit(e8, "Close of history file failed");
247        }
248    }
249
250    public void populateTable(Database dbp,
251                              int start_id, int balance, int nrecs, String msg) {
252        Defrec drec = new Defrec();
253
254        DatabaseEntry kdbt = new DatabaseEntry(drec.data);
255        kdbt.setSize(4);                  // sizeof(int)
256        DatabaseEntry ddbt = new DatabaseEntry(drec.data);
257        ddbt.setSize(drec.data.length);   // uses whole array
258
259        try {
260            for (int i = 0; i < nrecs; i++) {
261                kdbt.setRecordNumber(start_id + (int)i);
262                drec.set_balance(balance);
263                dbp.putNoOverwrite(null, kdbt, ddbt);
264            }
265        } catch (DatabaseException dbe) {
266            System.err.println("Failure initializing " + msg + " file: " +
267                               dbe.toString());
268            System.exit(1);
269        }
270    }
271
272    public void populateHistory(Database dbp) {
273        Histrec hrec = new Histrec();
274        hrec.set_amount(10);
275
276        byte[] arr = new byte[4];                  // sizeof(int)
277        int i;
278        DatabaseEntry kdbt = new DatabaseEntry(arr);
279        kdbt.setSize(arr.length);
280        DatabaseEntry ddbt = new DatabaseEntry(hrec.data);
281        ddbt.setSize(hrec.data.length);
282
283        try {
284            for (i = 1; i <= history; i++) {
285                kdbt.setRecordNumber(i);
286
287                hrec.set_aid(random_id(ACCOUNT));
288                hrec.set_bid(random_id(BRANCH));
289                hrec.set_tid(random_id(TELLER));
290
291                dbp.append(null, kdbt, ddbt);
292            }
293        } catch (DatabaseException dbe) {
294            errExit(dbe, "Failure initializing history file");
295        }
296    }
297
298    static Random rand = new Random();
299    public static int random_int(int lo, int hi) {
300        int t = rand.nextInt();
301        if (t < 0)
302            t = -t;
303        int ret = (int)(((double)t / ((double)(Integer.MAX_VALUE) + 1)) *
304            (hi - lo + 1));
305        ret += lo;
306        return (ret);
307    }
308
309    public int random_id(int type) {
310        int min, max, num;
311
312        max = min = BEGID;
313        num = accounts;
314        switch(type) {
315        case TELLER:
316            min += branches;
317            num = tellers;
318            // fallthrough
319        case BRANCH:
320            if (type == BRANCH)
321                num = branches;
322            min += accounts;
323            // fallthrough
324        case ACCOUNT:
325            max = min + num - 1;
326        }
327        return (random_int(min, max));
328    }
329
330    // The byte order is our choice.
331    //
332    static long get_int_in_array(byte[] array, int offset) {
333        return
334            ((0xff & array[offset + 0]) << 0)  |
335            ((0xff & array[offset + 1]) << 8)  |
336            ((0xff & array[offset + 2]) << 16) |
337            ((0xff & array[offset + 3]) << 24);
338    }
339
340    // Note: Value needs to be long to avoid sign extension
341    static void set_int_in_array(byte[] array, int offset, long value) {
342        array[offset + 0] = (byte)((value >> 0) & 0xff);
343        array[offset + 1] = (byte)((value >> 8) & 0xff);
344        array[offset + 2] = (byte)((value >> 16) & 0xff);
345        array[offset + 3] = (byte)((value >> 24) & 0xff);
346    }
347
348    // round 'd' to 'scale' digits, and return result as string
349    static String showRounded(double d, int scale) {
350        return new BigDecimal(d).
351            setScale(scale, BigDecimal.ROUND_HALF_DOWN).toString();
352    }
353
354    public void run(int ntxns, int threads) {
355        double gtps;
356        int txns, failed;
357        long curtime, starttime;
358        TxnThread[] txnList = new TxnThread[threads];
359        for (int i = 0; i < threads; i++)
360            txnList[i] = new TxnThread("Thread " + String.valueOf(i), ntxns);
361
362        starttime = (new Date()).getTime();
363        for (int i = 0; i < threads; i++)
364            txnList[i].start();
365        for (int i = 0; i < threads; i++)
366            try {
367                txnList[i].join();
368            } catch (Exception e1) {
369                errExit(e1, "join failed");
370            }
371
372        curtime = (new Date()).getTime();
373        txns = failed = 0;
374        for (int i = 0; i < threads; i++) {
375            txns += txnList[i].txns;
376            failed += txnList[i].failed;
377        }
378        gtps = (double)(txns - failed) /
379            ((curtime - starttime) / 1000.0);
380        System.out.print("\nTotal: " +
381                         String.valueOf(txns) + " txns " +
382                         String.valueOf(failed) + " failed ");
383        System.out.println(showRounded(gtps, 2) + " TPS");
384    }
385
386    class TxnThread extends Thread {
387        private int ntxns;       /* Number of txns we were asked to run. */
388        public int txns, failed; /* Number that succeeded / failed. */
389        private Database adb, bdb, hdb, tdb;
390
391        public TxnThread(String name, int ntxns) {
392            super(name);
393            this.ntxns = ntxns;
394        }
395
396        public void run() {
397            double gtps, itps;
398            int n, ret;
399            long start_time, end_time;
400
401            //
402            // Open the database files.
403            //
404            int err;
405            try {
406                DatabaseConfig config = new DatabaseConfig();
407                config.setTransactional(true);
408                adb = dbenv.openDatabase(null, "account", null, config);
409                bdb = dbenv.openDatabase(null, "branch", null, config);
410                tdb = dbenv.openDatabase(null, "teller", null, config);
411                hdb = dbenv.openDatabase(null, "history", null, config);
412            } catch (DatabaseException dbe) {
413                TpcbExample.errExit(dbe, "Open of db files failed");
414            } catch (FileNotFoundException fnfe) {
415                TpcbExample.errExit(fnfe, "Open of db files failed, missing file");
416            }
417
418            start_time = (new Date()).getTime();
419            for (txns = n = ntxns, failed = 0; n-- > 0;)
420                if ((ret = txn()) != 0)
421                    failed++;
422            end_time = (new Date()).getTime();
423            if (end_time == start_time)
424                end_time++;
425
426            System.out.println(getName() + ": " + (long)txns + " txns: " +
427                failed + " failed, " + TpcbExample.showRounded(
428                (txns - failed) / (double)(end_time - start_time), 2) + " TPS");
429
430            try {
431                adb.close();
432                bdb.close();
433                tdb.close();
434                hdb.close();
435            } catch (DatabaseException dbe2) {
436                TpcbExample.errExit(dbe2, "Close of db files failed");
437            }
438        }
439
440        //
441        // XXX Figure out the appropriate way to pick out IDs.
442        //
443        int txn() {
444            Cursor acurs = null;
445            Cursor bcurs = null;
446            Cursor hcurs = null;
447            Cursor tcurs = null;
448            Transaction t = null;
449
450            Defrec rec = new Defrec();
451            Histrec hrec = new Histrec();
452            int account, branch, teller;
453
454            DatabaseEntry d_dbt = new DatabaseEntry();
455            DatabaseEntry d_histdbt = new DatabaseEntry();
456            DatabaseEntry k_dbt = new DatabaseEntry();
457            DatabaseEntry k_histdbt = new DatabaseEntry();
458
459            account = TpcbExample.this.random_id(TpcbExample.ACCOUNT);
460            branch = TpcbExample.this.random_id(TpcbExample.BRANCH);
461            teller = TpcbExample.this.random_id(TpcbExample.TELLER);
462
463            // The history key will not actually be retrieved,
464            // but it does need to be set to something.
465            byte[] hist_key = new byte[4];
466            k_histdbt.setData(hist_key);
467            k_histdbt.setSize(4 /* == sizeof(int)*/);
468
469            byte[] key_bytes = new byte[4];
470            k_dbt.setData(key_bytes);
471            k_dbt.setSize(4 /* == sizeof(int)*/);
472
473            d_dbt.setData(rec.data);
474            d_dbt.setUserBuffer(rec.length(), true);
475
476            hrec.set_aid(account);
477            hrec.set_bid(branch);
478            hrec.set_tid(teller);
479            hrec.set_amount(10);
480            // Request 0 bytes since we're just positioning.
481            d_histdbt.setPartial(0, 0, true);
482
483            // START PER-TRANSACTION TIMING.
484            //
485            // Technically, TPCB requires a limit on response time, you only
486            // get to count transactions that complete within 2 seconds.
487            // That's not an issue for this sample application -- regardless,
488            // here's where the transaction begins.
489            try {
490                t = dbenv.beginTransaction(null, null);
491
492                acurs = adb.openCursor(t, null);
493                bcurs = bdb.openCursor(t, null);
494                tcurs = tdb.openCursor(t, null);
495                hcurs = hdb.openCursor(t, null);
496
497                // Account record
498                k_dbt.setRecordNumber(account);
499                if (acurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
500                    throw new Exception("acurs get failed");
501                rec.set_balance(rec.get_balance() + 10);
502                acurs.putCurrent(d_dbt);
503
504                // Branch record
505                k_dbt.setRecordNumber(branch);
506                if (bcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
507                    throw new Exception("bcurs get failed");
508                rec.set_balance(rec.get_balance() + 10);
509                bcurs.putCurrent(d_dbt);
510
511                // Teller record
512                k_dbt.setRecordNumber(teller);
513                if (tcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
514                    throw new Exception("ccurs get failed");
515                rec.set_balance(rec.get_balance() + 10);
516                tcurs.putCurrent(d_dbt);
517
518                // History record
519                d_histdbt.setPartial(0, 0, false);
520                d_histdbt.setData(hrec.data);
521                d_histdbt.setUserBuffer(hrec.length(), true);
522                if (hdb.append(t, k_histdbt, d_histdbt) != OperationStatus.SUCCESS)
523                    throw new DatabaseException("put failed");
524
525                acurs.close();
526                acurs = null;
527                bcurs.close();
528                bcurs = null;
529                tcurs.close();
530                tcurs = null;
531                hcurs.close();
532                hcurs = null;
533
534                // null out t in advance; if the commit fails,
535                // we don't want to abort it in the catch clause.
536                Transaction tmptxn = t;
537                t = null;
538                tmptxn.commit();
539
540                // END TIMING
541                return (0);
542            } catch (Exception e) {
543                try {
544                    if (acurs != null)
545                        acurs.close();
546                    if (bcurs != null)
547                        bcurs.close();
548                    if (tcurs != null)
549                        tcurs.close();
550                    if (hcurs != null)
551                        hcurs.close();
552                    if (t != null)
553                        t.abort();
554                } catch (DatabaseException dbe) {
555                    // not much we can do here.
556                }
557
558                if (TpcbExample.this.verbose) {
559                    System.out.println("Transaction A=" + String.valueOf(account) +
560                                       " B=" + String.valueOf(branch) +
561                                       " T=" + String.valueOf(teller) +
562                                       " failed");
563                    System.out.println("Reason: " + e.toString());
564                }
565                return (-1);
566            }
567        }
568    }
569
570    private static void usage() {
571        System.err.println(
572               "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n" +
573               "                   [-c cachesize] [-h home] [-n transactions]\n" +
574               "                   [-T threads] [-S seed] [-s history] [-t tellers]");
575        System.exit(1);
576    }
577
578    private static void invarg(String str) {
579        System.err.println("TpcbExample: invalid argument: " + str);
580        System.exit(1);
581    }
582
583    public static void errExit(Exception err, String s) {
584        System.err.print(progname + ": ");
585        if (s != null) {
586            System.err.print(s + ": ");
587        }
588        System.err.println(err.toString());
589        System.exit(1);
590    }
591
592    public static void main(String[] argv) throws java.io.IOException {
593        File home = new File("TESTDIR");
594        int accounts = ACCOUNTS;
595        int branches = BRANCHES;
596        int tellers = TELLERS;
597        int history = HISTORY;
598        int threads = 1;
599        int mpool = 0;
600        int ntxns = 0;
601        boolean iflag = false;
602        boolean txn_no_sync = false;
603        long seed = (new GregorianCalendar()).get(Calendar.SECOND);
604
605        for (int i = 0; i < argv.length; ++i) {
606            if (argv[i].equals("-a")) {
607                // Number of account records
608                if ((accounts = Integer.parseInt(argv[++i])) <= 0)
609                    invarg(argv[i]);
610            } else if (argv[i].equals("-b")) {
611                // Number of branch records
612                if ((branches = Integer.parseInt(argv[++i])) <= 0)
613                    invarg(argv[i]);
614            } else if (argv[i].equals("-c")) {
615                // Cachesize in bytes
616                if ((mpool = Integer.parseInt(argv[++i])) <= 0)
617                    invarg(argv[i]);
618            } else if (argv[i].equals("-f")) {
619                // Fast mode: no txn sync.
620                txn_no_sync = true;
621            } else if (argv[i].equals("-h")) {
622                // DB  home.
623                home = new File(argv[++i]);
624            } else if (argv[i].equals("-i")) {
625                // Initialize the test.
626                iflag = true;
627            } else if (argv[i].equals("-n")) {
628                // Number of transactions
629                if ((ntxns = Integer.parseInt(argv[++i])) <= 0)
630                    invarg(argv[i]);
631            } else if (argv[i].equals("-S")) {
632                // Random number seed.
633                seed = Long.parseLong(argv[++i]);
634                if (seed <= 0)
635                    invarg(argv[i]);
636            } else if (argv[i].equals("-s")) {
637                // Number of history records
638                if ((history = Integer.parseInt(argv[++i])) <= 0)
639                    invarg(argv[i]);
640            } else if (argv[i].equals("-T")) {
641                // Number of threads
642                if ((threads = Integer.parseInt(argv[++i])) <= 0)
643                    invarg(argv[i]);
644            } else if (argv[i].equals("-t")) {
645                // Number of teller records
646                if ((tellers = Integer.parseInt(argv[++i])) <= 0)
647                    invarg(argv[i]);
648            } else if (argv[i].equals("-v")) {
649                // Verbose option.
650                verbose = true;
651            } else {
652                usage();
653            }
654        }
655
656        rand.setSeed((int)seed);
657
658        // Initialize the database environment.
659        // Must be done in within a try block.
660        //
661        TpcbExample app = null;
662        try {
663            app = new TpcbExample(home, accounts, branches, tellers, history,
664                                  mpool, iflag || txn_no_sync);
665        } catch (Exception e1) {
666            errExit(e1, "initializing environment failed");
667        }
668
669        if (verbose)
670            System.out.println((long)accounts + " Accounts, " +
671                               String.valueOf(branches) + " Branches, " +
672                               String.valueOf(tellers) + " Tellers, " +
673                               String.valueOf(history) + " History");
674
675        if (iflag) {
676            if (ntxns != 0)
677                usage();
678            app.populate();
679        } else {
680            if (ntxns == 0)
681                usage();
682            app.run(ntxns, threads);
683        }
684
685        // Shut down the application.
686
687        try {
688            app.close();
689        } catch (DatabaseException dbe2) {
690            errExit(dbe2, "appexit failed");
691        }
692
693        System.exit(0);
694    }
695};
696
697// Simulate the following C struct:
698// struct Defrec {
699//     u_int32_t   id;
700//     u_int32_t   balance;
701//     u_int8_t    pad[RECLEN - sizeof(int) - sizeof(int)];
702// };
703
704class Defrec {
705    public Defrec() {
706        data = new byte[TpcbExample.RECLEN];
707    }
708
709    public int length() {
710        return TpcbExample.RECLEN;
711    }
712
713    public long get_id() {
714        return TpcbExample.get_int_in_array(data, 0);
715    }
716
717    public void set_id(long value) {
718        TpcbExample.set_int_in_array(data, 0, value);
719    }
720
721    public long get_balance() {
722        return TpcbExample.get_int_in_array(data, 4);
723    }
724
725    public void set_balance(long value) {
726        TpcbExample.set_int_in_array(data, 4, value);
727    }
728
729    static {
730        Defrec d = new Defrec();
731        d.set_balance(500000);
732    }
733
734    public byte[] data;
735}
736
737// Simulate the following C struct:
738// struct Histrec {
739//     u_int32_t   aid;
740//     u_int32_t   bid;
741//     u_int32_t   tid;
742//     u_int32_t   amount;
743//     u_int8_t    pad[RECLEN - 4 * sizeof(u_int32_t)];
744// };
745
746class Histrec {
747    public Histrec() {
748        data = new byte[TpcbExample.RECLEN];
749    }
750
751    public int length() {
752        return TpcbExample.RECLEN;
753    }
754
755    public long get_aid() {
756        return TpcbExample.get_int_in_array(data, 0);
757    }
758
759    public void set_aid(long value) {
760        TpcbExample.set_int_in_array(data, 0, value);
761    }
762
763    public long get_bid() {
764        return TpcbExample.get_int_in_array(data, 4);
765    }
766
767    public void set_bid(long value) {
768        TpcbExample.set_int_in_array(data, 4, value);
769    }
770
771    public long get_tid() {
772        return TpcbExample.get_int_in_array(data, 8);
773    }
774
775    public void set_tid(long value) {
776        TpcbExample.set_int_in_array(data, 8, value);
777    }
778
779    public long get_amount() {
780        return TpcbExample.get_int_in_array(data, 12);
781    }
782
783    public void set_amount(long value) {
784        TpcbExample.set_int_in_array(data, 12, value);
785    }
786
787    public byte[] data;
788}
789