1 /*-
2  * Copyright (c) 1997, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file EXAMPLES-LICENSE for license information.
5  *
6  */
7 
8 package db;
9 
10 import com.sleepycat.db.*;
11 
12 import java.io.File;
13 import java.io.FileNotFoundException;
14 import java.math.BigDecimal;
15 import java.util.Calendar;
16 import java.util.Date;
17 import java.util.Random;
18 import java.util.GregorianCalendar;
19 
20 //
21 // This program implements a basic TPC/B driver program.  To create the
22 // TPC/B database, run with the -i (init) flag.  The number of records
23 // with which to populate the account, history, branch, and teller tables
24 // is specified by the a, s, b, and t flags respectively.  To run a TPC/B
25 // test, use the n flag to indicate a number of transactions to run in
26 // each thread and -T to specify the number of threads.
27 //
28 class TpcbExample {
29     public static final int TELLERS_PER_BRANCH = 10;
30     public static final int ACCOUNTS_PER_TELLER = 10000;
31     public static final int HISTORY_PER_BRANCH = 2592000;
32 
33     //
34     // The default configuration that adheres to TPCB scaling rules requires
35     // nearly 3 GB of space.  To avoid requiring that much space for testing,
36     // we set the parameters much lower.  If you want to run a valid 10 TPS
37     // configuration, uncomment the VALID_SCALING configuration
38     //
39 
40     // VALID_SCALING configuration
41     /*
42       public static final int ACCOUNTS = 1000000;
43       public static final int BRANCHES = 10;
44       public static final int TELLERS = 100;
45       public static final int HISTORY = 25920000;
46     */
47 
48     // TINY configuration
49     /*
50       public static final int ACCOUNTS = 1000;
51       public static final int BRANCHES = 10;
52       public static final int TELLERS = 100;
53       public static final int HISTORY = 10000;
54     */
55 
56     // Default configuration
57     public static final int ACCOUNTS = 100000;
58     public static final int BRANCHES = 10;
59     public static final int TELLERS = 100;
60     public static final int HISTORY = 259200;
61 
62     public static final int HISTORY_LEN = 100;
63     public static final int RECLEN = 100;
64     public static final int BEGID = 1000000;
65 
66     // used by random_id()
67     public static final int ACCOUNT = 0;
68     public static final int BRANCH = 1;
69     public static final int TELLER = 2;
70 
71     public static boolean verbose = false;
72     public static final String progname = "TpcbExample";    // Program name.
73 
74     Environment dbenv;
75     int accounts, branches, tellers, history;
76 
TpcbExample(File home, int accounts, int branches, int tellers, int history, int cachesize, boolean noSync)77     public TpcbExample(File home,
78                        int accounts, int branches, int tellers, int history,
79                        int cachesize, boolean noSync)
80         throws DatabaseException, FileNotFoundException {
81 
82         this.accounts = accounts;
83         this.branches = branches;
84         this.tellers = tellers;
85         this.history = history;
86 
87         EnvironmentConfig config = new EnvironmentConfig();
88         config.setErrorStream(System.err);
89         config.setErrorPrefix(progname);
90         config.setLockDetectMode(LockDetectMode.DEFAULT);
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 
close()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     //
populate()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 (DatabaseException | FileNotFoundException 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 (DatabaseException | FileNotFoundException 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 (DatabaseException | FileNotFoundException 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 (DatabaseException | FileNotFoundException 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 
populateTable(Database dbp, int start_id, int balance, int nrecs, String msg)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 
populateHistory(Database dbp)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();
random_int(int lo, int hi)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 
random_id(int type)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     //
get_int_in_array(byte[] array, int offset)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
set_int_in_array(byte[] array, int offset, long value)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
showRounded(double d, int scale)349     static String showRounded(double d, int scale) {
350         return new BigDecimal(d).
351             setScale(scale, BigDecimal.ROUND_HALF_DOWN).toString();
352     }
353 
run(int ntxns, int threads)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 final 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 
TxnThread(String name, int ntxns)391         public TxnThread(String name, int ntxns) {
392             super(name);
393             this.ntxns = ntxns;
394         }
395 
396         @Override
run()397         public void run() {
398             double gtps, itps;
399             int n, ret;
400             long start_time, end_time;
401 
402             //
403             // Open the database files.
404             //
405             int err;
406             try {
407                 DatabaseConfig config = new DatabaseConfig();
408                 config.setTransactional(true);
409                 adb = dbenv.openDatabase(null, "account", null, config);
410                 bdb = dbenv.openDatabase(null, "branch", null, config);
411                 tdb = dbenv.openDatabase(null, "teller", null, config);
412                 hdb = dbenv.openDatabase(null, "history", null, config);
413             } catch (DatabaseException dbe) {
414                 TpcbExample.errExit(dbe, "Open of db files failed");
415             } catch (FileNotFoundException fnfe) {
416                 TpcbExample.errExit(fnfe, "Open of db files failed, missing file");
417             }
418 
419             start_time = (new Date()).getTime();
420             for (txns = n = ntxns, failed = 0; n-- > 0;)
421                 if ((ret = txn()) != 0)
422                     failed++;
423             end_time = (new Date()).getTime();
424             if (end_time == start_time)
425                 end_time++;
426 
427             System.out.println(getName() + ": " + (long)txns + " txns: " +
428                 failed + " failed, " + TpcbExample.showRounded(
429                 (txns - failed) / (double)(end_time - start_time), 2) + " TPS");
430 
431             try {
432                 adb.close();
433                 bdb.close();
434                 tdb.close();
435                 hdb.close();
436             } catch (DatabaseException dbe2) {
437                 TpcbExample.errExit(dbe2, "Close of db files failed");
438             }
439         }
440 
441         //
442         // XXX Figure out the appropriate way to pick out IDs.
443         //
txn()444         int txn() {
445             Cursor acurs = null;
446             Cursor bcurs = null;
447             Cursor hcurs = null;
448             Cursor tcurs = null;
449             Transaction t = null;
450 
451             Defrec rec = new Defrec();
452             Histrec hrec = new Histrec();
453             int account, branch, teller;
454 
455             DatabaseEntry d_dbt = new DatabaseEntry();
456             DatabaseEntry d_histdbt = new DatabaseEntry();
457             DatabaseEntry k_dbt = new DatabaseEntry();
458             DatabaseEntry k_histdbt = new DatabaseEntry();
459 
460             account = TpcbExample.this.random_id(TpcbExample.ACCOUNT);
461             branch = TpcbExample.this.random_id(TpcbExample.BRANCH);
462             teller = TpcbExample.this.random_id(TpcbExample.TELLER);
463 
464             // The history key will not actually be retrieved,
465             // but it does need to be set to something.
466             byte[] hist_key = new byte[4];
467             k_histdbt.setData(hist_key);
468             k_histdbt.setSize(4 /* == sizeof(int)*/);
469 
470             byte[] key_bytes = new byte[4];
471             k_dbt.setData(key_bytes);
472             k_dbt.setSize(4 /* == sizeof(int)*/);
473 
474             d_dbt.setData(rec.data);
475             d_dbt.setUserBuffer(rec.length(), true);
476 
477             hrec.set_aid(account);
478             hrec.set_bid(branch);
479             hrec.set_tid(teller);
480             hrec.set_amount(10);
481             // Request 0 bytes since we're just positioning.
482             d_histdbt.setPartial(0, 0, true);
483 
484             // START PER-TRANSACTION TIMING.
485             //
486             // Technically, TPCB requires a limit on response time, you only
487             // get to count transactions that complete within 2 seconds.
488             // That's not an issue for this sample application -- regardless,
489             // here's where the transaction begins.
490             try {
491                 t = dbenv.beginTransaction(null, null);
492 
493                 acurs = adb.openCursor(t, null);
494                 bcurs = bdb.openCursor(t, null);
495                 tcurs = tdb.openCursor(t, null);
496                 hcurs = hdb.openCursor(t, null);
497 
498                 // Account record
499                 k_dbt.setRecordNumber(account);
500                 if (acurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
501                     throw new Exception("acurs get failed");
502                 rec.set_balance(rec.get_balance() + 10);
503                 acurs.putCurrent(d_dbt);
504 
505                 // Branch record
506                 k_dbt.setRecordNumber(branch);
507                 if (bcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
508                     throw new Exception("bcurs get failed");
509                 rec.set_balance(rec.get_balance() + 10);
510                 bcurs.putCurrent(d_dbt);
511 
512                 // Teller record
513                 k_dbt.setRecordNumber(teller);
514                 if (tcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
515                     throw new Exception("ccurs get failed");
516                 rec.set_balance(rec.get_balance() + 10);
517                 tcurs.putCurrent(d_dbt);
518 
519                 // History record
520                 d_histdbt.setPartial(0, 0, false);
521                 d_histdbt.setData(hrec.data);
522                 d_histdbt.setUserBuffer(hrec.length(), true);
523                 if (hdb.append(t, k_histdbt, d_histdbt) != OperationStatus.SUCCESS)
524                     throw new DatabaseException("put failed");
525 
526                 acurs.close();
527                 acurs = null;
528                 bcurs.close();
529                 bcurs = null;
530                 tcurs.close();
531                 tcurs = null;
532                 hcurs.close();
533                 hcurs = null;
534 
535                 // null out t in advance; if the commit fails,
536                 // we don't want to abort it in the catch clause.
537                 Transaction tmptxn = t;
538                 t = null;
539                 tmptxn.commit();
540 
541                 // END TIMING
542                 return (0);
543             } catch (Exception e) {
544                 try {
545                     if (acurs != null)
546                         acurs.close();
547                     if (bcurs != null)
548                         bcurs.close();
549                     if (tcurs != null)
550                         tcurs.close();
551                     if (hcurs != null)
552                         hcurs.close();
553                     if (t != null)
554                         t.abort();
555                 } catch (DatabaseException dbe) {
556                     // not much we can do here.
557                 }
558 
559                 if (TpcbExample.this.verbose) {
560                     System.out.println("Transaction A=" + String.valueOf(account) +
561                                        " B=" + String.valueOf(branch) +
562                                        " T=" + String.valueOf(teller) +
563                                        " failed");
564                     System.out.println("Reason: " + e.toString());
565                 }
566                 return (-1);
567             }
568         }
569     }
570 
usage()571     private static void usage() {
572         System.err.println(
573                "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n" +
574                "                   [-c cachesize] [-h home] [-n transactions]\n" +
575                "                   [-T threads] [-S seed] [-s history] [-t tellers]");
576         System.exit(1);
577     }
578 
invarg(String str)579     private static void invarg(String str) {
580         System.err.println("TpcbExample: invalid argument: " + str);
581         System.exit(1);
582     }
583 
errExit(Exception err, String s)584     public static void errExit(Exception err, String s) {
585         System.err.print(progname + ": ");
586         if (s != null) {
587             System.err.print(s + ": ");
588         }
589         System.err.println(err.toString());
590         System.exit(1);
591     }
592 
main(String[] argv)593     public static void main(String[] argv) throws java.io.IOException {
594         File home = new File("TESTDIR");
595         int accounts = ACCOUNTS;
596         int branches = BRANCHES;
597         int tellers = TELLERS;
598         int history = HISTORY;
599         int threads = 1;
600         int mpool = 0;
601         int ntxns = 0;
602         boolean iflag = false;
603         boolean txn_no_sync = false;
604         long seed = (new GregorianCalendar()).get(Calendar.SECOND);
605 
606         for (int i = 0; i < argv.length; ++i) {
607             switch (argv[i]) {
608                 case "-a":
609                     // Number of account records
610                     if ((accounts = Integer.parseInt(argv[++i])) <= 0)
611                         invarg(argv[i]);
612                     break;
613                 case "-b":
614                     // Number of branch records
615                     if ((branches = Integer.parseInt(argv[++i])) <= 0)
616                         invarg(argv[i]);
617                     break;
618                 case "-c":
619                     // Cachesize in bytes
620                     if ((mpool = Integer.parseInt(argv[++i])) <= 0)
621                         invarg(argv[i]);
622                     break;
623                 case "-f":
624                     // Fast mode: no txn sync.
625                     txn_no_sync = true;
626                     break;
627                 case "-h":
628                     // DB  home.
629                     home = new File(argv[++i]);
630                     break;
631                 case "-i":
632                     // Initialize the test.
633                     iflag = true;
634                     break;
635                 case "-n":
636                     // Number of transactions
637                     if ((ntxns = Integer.parseInt(argv[++i])) <= 0)
638                         invarg(argv[i]);
639                     break;
640                 case "-S":
641                     // Random number seed.
642                     seed = Long.parseLong(argv[++i]);
643                     if (seed <= 0)
644                         invarg(argv[i]);
645                     break;
646                 case "-s":
647                     // Number of history records
648                     if ((history = Integer.parseInt(argv[++i])) <= 0)
649                         invarg(argv[i]);
650                     break;
651                 case "-T":
652                     // Number of threads
653                     if ((threads = Integer.parseInt(argv[++i])) <= 0)
654                         invarg(argv[i]);
655                     break;
656                 case "-t":
657                     // Number of teller records
658                     if ((tellers = Integer.parseInt(argv[++i])) <= 0)
659                         invarg(argv[i]);
660                     break;
661                 case "-v":
662                     // Verbose option.
663                     verbose = true;
664                     break;
665                 default:
666                     usage();
667                     break;
668             }
669         }
670 
671         rand.setSeed((int)seed);
672 
673         // Initialize the database environment.
674         // Must be done in within a try block.
675         //
676         TpcbExample app = null;
677         try {
678             app = new TpcbExample(home, accounts, branches, tellers, history,
679                                   mpool, iflag || txn_no_sync);
680         } catch (DatabaseException | FileNotFoundException e1) {
681             errExit(e1, "initializing environment failed");
682         }
683 
684         if (verbose)
685             System.out.println((long)accounts + " Accounts, " +
686                                String.valueOf(branches) + " Branches, " +
687                                String.valueOf(tellers) + " Tellers, " +
688                                String.valueOf(history) + " History");
689 
690         if (iflag) {
691             if (ntxns != 0)
692                 usage();
693             app.populate();
694         } else {
695             if (ntxns == 0)
696                 usage();
697             app.run(ntxns, threads);
698         }
699 
700         // Shut down the application.
701 
702         try {
703             app.close();
704         } catch (DatabaseException dbe2) {
705             errExit(dbe2, "appexit failed");
706         }
707 
708         System.exit(0);
709     }
710 };
711 
712 // Simulate the following C struct:
713 // struct Defrec {
714 //     u_int32_t   id;
715 //     u_int32_t   balance;
716 //     u_int8_t    pad[RECLEN - sizeof(int) - sizeof(int)];
717 // };
718 
719 class Defrec {
Defrec()720     public Defrec() {
721         data = new byte[TpcbExample.RECLEN];
722     }
723 
length()724     public int length() {
725         return TpcbExample.RECLEN;
726     }
727 
get_id()728     public long get_id() {
729         return TpcbExample.get_int_in_array(data, 0);
730     }
731 
set_id(long value)732     public void set_id(long value) {
733         TpcbExample.set_int_in_array(data, 0, value);
734     }
735 
get_balance()736     public long get_balance() {
737         return TpcbExample.get_int_in_array(data, 4);
738     }
739 
set_balance(long value)740     public void set_balance(long value) {
741         TpcbExample.set_int_in_array(data, 4, value);
742     }
743 
744     static {
745         Defrec d = new Defrec();
746         d.set_balance(500000);
747     }
748 
749     public byte[] data;
750 }
751 
752 // Simulate the following C struct:
753 // struct Histrec {
754 //     u_int32_t   aid;
755 //     u_int32_t   bid;
756 //     u_int32_t   tid;
757 //     u_int32_t   amount;
758 //     u_int8_t    pad[RECLEN - 4 * sizeof(u_int32_t)];
759 // };
760 
761 class Histrec {
Histrec()762     public Histrec() {
763         data = new byte[TpcbExample.RECLEN];
764     }
765 
length()766     public int length() {
767         return TpcbExample.RECLEN;
768     }
769 
get_aid()770     public long get_aid() {
771         return TpcbExample.get_int_in_array(data, 0);
772     }
773 
set_aid(long value)774     public void set_aid(long value) {
775         TpcbExample.set_int_in_array(data, 0, value);
776     }
777 
get_bid()778     public long get_bid() {
779         return TpcbExample.get_int_in_array(data, 4);
780     }
781 
set_bid(long value)782     public void set_bid(long value) {
783         TpcbExample.set_int_in_array(data, 4, value);
784     }
785 
get_tid()786     public long get_tid() {
787         return TpcbExample.get_int_in_array(data, 8);
788     }
789 
set_tid(long value)790     public void set_tid(long value) {
791         TpcbExample.set_int_in_array(data, 8, value);
792     }
793 
get_amount()794     public long get_amount() {
795         return TpcbExample.get_int_in_array(data, 12);
796     }
797 
set_amount(long value)798     public void set_amount(long value) {
799         TpcbExample.set_int_in_array(data, 12, value);
800     }
801 
802     public byte[] data;
803 }
804