Compare commits

...

610 Commits

Author SHA1 Message Date
Eyck Jentzsch 97d6062c3f Merge branch 'release/2.19.4' 2024-02-11 12:59:13 +01:00
Eyck Jentzsch 92428a3859 update version numbers 2024-02-11 12:58:44 +01:00
Eyck Jentzsch 5f244dc03a fixes an multiple-load issue leading to NPE when adding empty generator
streams
2024-02-11 12:35:10 +01:00
Eyck Jentzsch 64cc09d207 updates plugin settings to conform with Eclipse 2023-12 2024-02-11 12:34:12 +01:00
Eyck Jentzsch 7af8b73bdd Merge branch 'release/2.19.3' 2024-01-27 15:11:44 +01:00
Eyck Jentzsch 61bc407fb3 updates version numbers 2024-01-27 15:05:04 +01:00
Eyck Jentzsch f6a3da013e fixes some drawing and state restore issues 2024-01-27 14:58:55 +01:00
Eyck Jentzsch c4fafae029 fixes a missing increment statement 2024-01-27 14:57:15 +01:00
Eyck Jentzsch 819e549d87 Merge branch 'release/2.19.1' 2024-01-24 06:31:00 +01:00
Eyck Jentzsch e472a092c3 updates version numbers 2024-01-23 17:51:02 +01:00
Eyck Jentzsch 523c266e8d fixes locking issue preventing reload 2024-01-23 17:50:43 +01:00
Eyck Jentzsch db640808f2 updates packaged java version 2023-09-14 16:29:02 +02:00
Eyck Jentzsch 09a55f1513 fixes some naming for the update site 2023-08-15 13:44:23 +02:00
Eyck Jentzsch 088500afff update update site address 2023-08-11 14:02:49 +02:00
Eyck Jentzsch 917a51ced7 Merge branch 'release/2.19.0' 2023-08-11 13:28:01 +02:00
Eyck Jentzsch c27354070c updates version numbers 2023-08-11 13:26:42 +02:00
Eyck Jentzsch 9ca573efdb Merge branch 'release/2.19' 2023-08-11 13:15:51 +02:00
Eyck Jentzsch 299c10363a Merge branch 'release/2.19' 2023-08-11 12:35:34 +02:00
Eyck Jentzsch 36fdc68051 updates version and eclipse rcp version 2023-08-11 12:34:54 +02:00
Eyck Jentzsch 53576f5e1b fixes load issue when using partial txlog files 2023-08-11 11:40:21 +02:00
Eyck Jentzsch abb8b45cd8 changes font handling for MultiBitStencil 2023-06-27 21:20:08 +02:00
Eyck Jentzsch e557e2c8d4 Merge branch 'develop' 2023-06-27 19:42:35 +02:00
Eyck Jentzsch aaf8a9e5d0 fixes bitvector conversion to hex number 2023-06-27 19:42:06 +02:00
Eyck Jentzsch c9a94334c4 fixes packaging to work without JS 2023-06-27 18:49:45 +02:00
Eyck Jentzsch 8e7e977ac5 Merge branch 'develop' 2023-06-27 17:36:37 +02:00
Eyck Jentzsch 24a362c277 adds some more test data 2023-06-27 17:33:10 +02:00
Eyck Jentzsch aa4659f8ef adds some more cleanup 2023-06-27 17:00:19 +02:00
Eyck Jentzsch 81f37d4740 puts some cleanup 2023-06-27 16:56:03 +02:00
Eyck Jentzsch 69c82ac9a8 adds some more test files 2023-06-27 16:29:26 +02:00
Eyck Jentzsch 95c04c2240 updates images 2023-06-27 16:19:14 +02:00
Eyck Jentzsch be87792dad uadds and pdates some graphics and menu items 2023-06-27 16:01:13 +02:00
Eyck Jentzsch d1808ec1cf updates README.md 2023-06-27 10:39:36 +02:00
Eyck Jentzsch 1a86104b19 fixes alignment issue in waveform canvas 2023-03-19 17:09:13 +01:00
Eyck Jentzsch dc4798b8ba implements visual improvements 2023-03-19 17:02:00 +01:00
Eyck Jentzsch 3890a87a8c adds rendering of hierarchy indicator 2023-03-19 11:29:16 +01:00
Eyck Jentzsch d657843541 improves bit vector rendering 2023-03-19 11:28:44 +01:00
Eyck Jentzsch 361a18b38e changes value representation in waves 2023-03-19 11:23:25 +01:00
Eyck Jentzsch 354c4a4390 fixes signed/unsigned conversion for large integer sizes 2023-03-18 18:29:34 +01:00
Eyck Jentzsch 65461ccc48 fixes unit test 2023-03-18 12:47:43 +01:00
Eyck Jentzsch 654cf3f9e5 fixes visual handling of Tx not yet being loaded 2023-03-18 12:20:29 +01:00
Eyck Jentzsch 6f2f5a388c fixes deferred loading of Tx when being referenced 2023-03-18 12:18:20 +01:00
Eyck Jentzsch f9d38b5091 adapts to extended database format 2023-03-18 10:13:10 +01:00
Eyck Jentzsch ba83c585f0 fixes missing block increment which prevents loading large dbs 2023-03-18 10:12:27 +01:00
Eyck Jentzsch 14425b9102 fixes FST timescale calculation 2023-03-15 16:53:40 +01:00
Eyck Jentzsch 1b7d4c3ed6 fixes FTR to delay tx chunk reading 2023-03-04 15:33:19 +01:00
Eyck Jentzsch b391d19303 updates time scale handling 2023-03-04 12:33:22 +01:00
Eyck Jentzsch bf38ccd6db updates test cases 2023-03-04 11:54:18 +01:00
Eyck Jentzsch 567d62cea1 Merge branch 'develop' into feature/waveform_groups 2023-03-01 20:00:41 +01:00
Eyck Jentzsch 6769513e2d fixes database reopening problem due to locking 2023-03-01 12:53:34 +01:00
Eyck Jentzsch f64a1baf02 rename BLANK to EMPY, enhances separator render, fixes menu selection 2023-03-01 09:21:11 +01:00
Eyck Jentzsch 9be9671d3a adds configurable label to separator 2023-02-28 23:05:39 +01:00
Eyck Jentzsch c39ca8884a renames blank to separator 2023-02-28 13:23:42 +01:00
Eyck Jentzsch 5670977d52 fixes color handling 2023-02-28 12:12:47 +01:00
Eyck Jentzsch 463dad60b9 fixes color preferences handling 2023-02-28 12:03:34 +01:00
Eyck Jentzsch 8a54ed3f6c Merge branch 'develop' into feature/waveform_groups 2023-02-28 11:30:12 +01:00
Eyck Jentzsch 74f30e921f fixes imports 2023-02-28 11:29:28 +01:00
Eyck Jentzsch dbae0f1666 Merge branch 'develop' into feature/waveform_groups 2023-02-28 11:27:51 +01:00
Eyck Jentzsch 52232bb0db adds blank entry for waveform viewer 2023-02-28 11:23:38 +01:00
Eyck Jentzsch 6dfe6c8e2b adds blank entry for waveform viewer 2023-02-28 07:50:10 +01:00
Eyck Jentzsch a18f72c43f updates and cleans messages 2023-02-28 07:22:42 +01:00
Eyck Jentzsch ec471348f6 adds binary render option 2023-02-28 07:06:48 +01:00
Eyck Jentzsch b268b4b677 updates gitignore 2023-02-27 20:31:22 +01:00
Eyck Jentzsch da2cd03606 updates version numbers, fixes missing pom.xml 2023-02-27 20:29:41 +01:00
Eyck Jentzsch c45d7b5024 add windows fst library 2023-02-27 19:50:18 +01:00
Eyck Jentzsch 1efb574a98 fixes missing dispose of resources 2023-02-27 15:56:15 +01:00
Eyck Jentzsch 0105621be3 adds UI support and fixes a reading issue 2023-02-27 15:16:55 +01:00
Eyck Jentzsch e44e4d0a05 adds working FST reader implementation 2023-02-27 13:09:38 +01:00
Eyck Jentzsch 299f76323f cleans interfaces up 2023-02-27 13:07:10 +01:00
Eyck Jentzsch bba4349e1e adds skeleton of FST plugin 2023-02-26 10:53:39 +01:00
Eyck Jentzsch 85d9c92f21 fixes a tree display issue in transaction details view 2023-02-24 11:28:40 +01:00
Eyck Jentzsch cf663bcae5 Merge branch 'release/2.17.3' 2023-02-24 09:15:23 +01:00
Eyck Jentzsch 2885cb9602 Merge branch 'release/2.17.3' into develop 2023-02-24 09:15:23 +01:00
Eyck Jentzsch 31e79e453c updates version number 2023-02-24 09:15:10 +01:00
Eyck Jentzsch 23a2f8d6c8 fixes data type numbering 2023-02-24 08:31:53 +01:00
Eyck Jentzsch ceaf52bfa1 fixes some divide by zero exception while drawing 2023-02-23 21:10:57 +01:00
Eyck Jentzsch 7a98eda464 Merge branch 'release/2.17.2' 2023-02-23 15:51:49 +01:00
Eyck Jentzsch c8e587c1ea updates version numbers 2023-02-23 15:50:47 +01:00
Eyck Jentzsch d95683a146 fixes FTR reading and makes it more robust 2023-02-23 13:33:32 +01:00
Eyck Jentzsch 07062d5618 updates target platform to 2022-12 2023-02-22 08:29:50 +01:00
Eyck Jentzsch e0b710efc4 sets target execution environment for launch configs 2023-02-22 07:56:28 +01:00
Eyck Jentzsch e0f174173c Merge branch 'release/2.17.1' 2023-02-22 07:47:27 +01:00
Eyck Jentzsch 12a7c701e3 cleans imports and other warnings 2023-02-22 07:45:22 +01:00
Eyck Jentzsch cb259503cb Merge branch 'release/2.17.1' into develop 2023-02-22 07:30:43 +01:00
Eyck Jentzsch 8aca26e499 Merge branch 'release/2.17.1' 2023-02-22 07:30:06 +01:00
Eyck Jentzsch ebf3f920bf updates version numbers 2023-02-22 07:29:26 +01:00
Eyck Jentzsch 3b57ec029b adds support for compressed FTR 2023-02-22 07:24:02 +01:00
Eyck Jentzsch 01c9b7f1c2 adds support for FTR chunk type 0 2023-02-22 06:14:17 +01:00
Eyck Jentzsch a737f5588d fixes entries in product definition 2023-02-21 20:03:59 +01:00
Eyck Jentzsch f7f512a5f3 Merge branch 'release/2.17.0' 2023-02-21 19:57:07 +01:00
Eyck Jentzsch 8f838a96b7 Merge branch 'release/2.17.0' into develop 2023-02-21 19:57:07 +01:00
Eyck Jentzsch 4d49e3b53f updates version numbers 2023-02-21 19:56:55 +01:00
Eyck Jentzsch b809042189 fixes removing traces 2023-02-21 19:24:13 +01:00
Eyck Jentzsch 0e705ce0e9 adds initial version of FTR reader 2023-02-21 17:59:11 +01:00
Eyck Jentzsch 25064f9744 externalizes strings 2022-09-30 15:29:16 +02:00
Eyck Jentzsch 31fd54c6be adds update functionality for SCViewer 2022-09-30 14:23:54 +02:00
Eyck Jentzsch 3a931d59d5 Merge branch 'release/2.16.1' 2022-06-12 20:58:31 +02:00
Eyck Jentzsch a1094ff870 Merge branch 'release/2.16.1' into develop 2022-06-12 20:58:31 +02:00
Eyck Jentzsch e53764cd7b update version numbers 2022-06-12 13:10:15 +02:00
Eyck Jentzsch 3f7bdc7e28 simplify and speedup txlog parsing 2022-06-12 11:40:23 +02:00
Eyck Jentzsch 8d94b517c1 fix file type detection for SCV files 2022-05-17 16:40:56 +02:00
Eyck Jentzsch d5e50a06b2 correct used eclipse version in update site generation
Merge branch 'release/2.16.0'
2022-05-16 22:28:00 +02:00
Eyck Jentzsch fda4c64ac7 Merge branch 'release/2.16.0' 2022-05-16 21:16:31 +02:00
Eyck Jentzsch 8dcfc061cb Merge branch 'release/2.16.0' into develop 2022-05-16 21:16:31 +02:00
Eyck Jentzsch 79a5343cde update version numbers 2022-05-16 21:13:00 +02:00
Eyck Jentzsch c9c6db8e4c update target platform to release 21-12 2022-05-16 21:07:32 +02:00
Eyck Jentzsch e629bdc5bc add LZ4 (de)compressor to target platform and add as input format 2022-05-16 20:34:23 +02:00
Eyck Jentzsch 8522627081 align adoc format and fix issues 2022-01-09 14:30:48 +01:00
Eyck Jentzsch d7f6d9b879 add second style for documentation 2022-01-09 14:02:05 +01:00
Eyck Jentzsch 8f48abf0ae cleanup package structure 2022-01-09 13:57:49 +01:00
Eyck Jentzsch fbec708522 fix geometry of file browser 2022-01-09 13:54:58 +01:00
Eyck Jentzsch 5c59fb0676 Merge branch 'feature/integrated_jre' into develop 2022-01-09 13:30:00 +01:00
Eyck Jentzsch 2a9c5ff6a5 add justj runtime 2022-01-09 13:29:10 +01:00
Eyck Jentzsch 9fe7a83dfa update target definition to 2021-09 2022-01-09 12:13:01 +01:00
Eyck Jentzsch eb4e2301c1 fix minimum required execution environment 2022-01-09 11:59:33 +01:00
Eyck Jentzsch a6aa9859ea update maven plugin versions 2022-01-09 11:52:25 +01:00
Eyck Jentzsch 1866ae17a9 add JavaSE-15 execution environment 2022-01-09 11:12:17 +01:00
Eyck Jentzsch 6987985e36 fix maven build 2022-01-09 08:57:45 +01:00
Eyck Jentzsch f81f84d60e cleanup of settings and add non-linux bundles to help feature 2022-01-09 08:44:59 +01:00
Eyck Jentzsch fd8e2ea751 finishing touches on help system 2022-01-08 19:26:22 +01:00
Eyck Jentzsch 2d11e39653 Merge branch 'feature/custom_scrollbar' into develop 2022-01-08 19:17:15 +01:00
Eyck Jentzsch ab419a743f Merge branch 'feature/help_system' into develop 2022-01-08 19:16:15 +01:00
Eyck Jentzsch fc64fa781a fix adoc syntax 2022-01-08 18:31:42 +01:00
Eyck Jentzsch c474ca33d8 use internal help browser 2022-01-08 18:31:27 +01:00
Eyck Jentzsch 8119c1a4f3 direct generation of help plugin 2022-01-08 17:36:52 +01:00
Eyck Jentzsch 5f84194145 first working version of help with dummy input 2022-01-08 17:24:26 +01:00
Eyck Jentzsch 7dbcffe95d fix layout for auto-hiding scrollbar (e.g. GTK) 2022-01-07 21:25:01 +01:00
Eyck Jentzsch 20934a9f47 fix behavior of ZoomBar used by WaveformCanvas 2022-01-07 19:42:27 +01:00
Eyck Jentzsch 24890f9bbb change behavior with a clear distinction
If no modifier key is used the zoombar acts as a scroll bar. If ctrl is
used it modifies the zoom level, shift is a speed modifier
2022-01-06 19:15:31 +01:00
Eyck Jentzsch bd0629301b add next increment - basically working 2022-01-06 17:48:40 +01:00
Eyck Jentzsch 71da420d86 checkpoint development 2022-01-05 21:51:57 +01:00
Eyck Jentzsch f9be6758e2 implement scrollbar adapter and first version of zooming scrollbar 2022-01-03 21:46:40 +01:00
Eyck Jentzsch 93a8c067fc add sliuder variant 2022-01-03 14:15:59 +01:00
Eyck Jentzsch 5a372d0f90 add first elements 2022-01-02 15:51:52 +01:00
Eyck Jentzsch 806000c4cc add missing icon 2021-12-14 21:16:11 +01:00
Eyck Jentzsch 539e5de813 Merge branch 'release/2.15.1' 2021-12-02 16:55:18 +01:00
Eyck Jentzsch c32d46cdc0 Merge branch 'release/2.15.1' into develop 2021-12-02 16:55:18 +01:00
Eyck Jentzsch 5736279e8d update version numbers 2021-12-02 16:55:07 +01:00
Eyck Jentzsch e3f4dc6616 fix loading of corrupted uncompressed SCV files 2021-12-02 16:47:12 +01:00
Eyck Jentzsch a64e06ff7a Merge branch 'master' into develop 2021-11-28 12:01:29 +01:00
Eyck Jentzsch a42b786835 fixe some naming 2021-11-28 11:39:51 +01:00
Eyck Jentzsch e76000d87b fix path for gh-pages checkout 2021-11-27 17:16:56 +01:00
Eyck Jentzsch 60ead71029 Merge branch 'release/2.15.0' into develop 2021-11-27 16:49:01 +01:00
Eyck Jentzsch 4c48fda5ad Merge branch 'release/2.15.0' 2021-11-27 16:49:01 +01:00
Eyck Jentzsch 6a591f2dbc add gh-pages deployment steps 2021-11-27 16:48:09 +01:00
Eyck Jentzsch 86c30ad948 update version numbers 2021-11-27 15:39:07 +01:00
Eyck Jentzsch 0ae055b486 Merge branch 'feature/continous_zoom' into develop 2021-11-27 15:22:44 +01:00
Eyck Jentzsch efcd4f5ab8 update tool bar actions and icons 2021-11-27 15:21:17 +01:00
Eyck Jentzsch 232d2d4275 move zomm/navigate toolbar to view 2021-11-27 14:31:39 +01:00
Eyck Jentzsch 44f96e5383 cleanup imports 2021-11-27 14:04:25 +01:00
Eyck Jentzsch 44812310b0 implement continous zoom and removed zoom level status control 2021-11-27 14:04:06 +01:00
Eyck Jentzsch 6ef91bb5e7 Merge branch 'release/2.14.2' 2021-11-18 14:31:09 +01:00
Eyck Jentzsch aa459b0ea6 Merge branch 'release/2.14.2' into develop 2021-11-18 14:31:09 +01:00
Eyck Jentzsch a2adf66618 update version numbers 2021-11-18 14:30:54 +01:00
Eyck Jentzsch 3405e90df9 fix behavior of help window 2021-11-18 14:24:08 +01:00
Eyck Jentzsch 9a59947e67 fix handling of key short cuts 2021-11-18 12:55:23 +01:00
Eyck Jentzsch 9384d3278c remove hierarchy in poms 2021-11-18 08:41:52 +01:00
Eyck Jentzsch 1af3171b2e cleanup imports 2021-11-16 14:15:33 +01:00
Eyck Jentzsch 03fd9e154b Merge branch 'master' into develop 2021-11-16 14:13:22 +01:00
Eyck Jentzsch 3572f683e3 add zoom levels 2021-11-16 14:12:10 +01:00
Eyck Jentzsch 524ffb189c fix MT race condition 2021-11-16 14:11:30 +01:00
Eyck Jentzsch 7fac6c8f74 Merge branch 'release/2.14.1' into develop 2021-11-16 11:32:41 +01:00
Eyck Jentzsch 037c645075 Merge branch 'release/2.14.1' 2021-11-16 11:32:41 +01:00
Eyck Jentzsch 2f9bd29dc8 update version numbers 2021-11-16 11:32:34 +01:00
Eyck Jentzsch 92662c546a fix zoom level calculation 2021-11-16 11:28:32 +01:00
Eyck Jentzsch efa6544623 cleanup 2021-11-16 11:27:58 +01:00
Eyck Jentzsch 52cf9daeec unify time printing (#69) 2021-11-16 09:37:50 +01:00
Eyck Jentzsch 4a315722b1 add help browser window instead of dialog 2021-11-16 09:33:09 +01:00
Eyck Jentzsch a52efd1a12 Merge branch 'master' into develop 2021-11-15 22:25:12 +01:00
Eyck Jentzsch 535df30ada Merge branch 'release/2.14.0' into develop 2021-11-15 21:38:51 +01:00
Eyck Jentzsch bad34dd1d1 Merge branch 'release/2.14.0' 2021-11-15 21:38:51 +01:00
Eyck Jentzsch 5ac7f05f57 Merge branch 'master' of https://git.minres.com/VP-Tools/SCViewer 2021-11-15 21:37:51 +01:00
Eyck Jentzsch 8353b59a27 update version numbers for release
This release fixes the following tickets:
* #62: Instead of or in addition to the Restore Default Zoom button, add
Full Zoom Out button.
* #64: Load multiple trace files and enable the possibility to view
signals from different files in a single window.
* partial #66: key shortcuts for actions like zoom in/out, go to
next/prev event etc
* #70: add full zoom and zoom between markers (once we had markers)
* #75: marker is often hiding events underneath for highlighting and
getting transaction details
* #82: Implement help for keyboard shortcuts
2021-11-15 21:29:08 +01:00
Eyck Jentzsch 2c6ca6c376 update version number to 2.14 2021-11-15 21:25:05 +01:00
Eyck Jentzsch f4b03cb0e6 extend zoom handling (#70) and hide tx property panes (#58) 2021-11-15 21:23:13 +01:00
Eyck Jentzsch c7858997c0 update README.md 2021-11-15 15:18:56 +01:00
Eyck Jentzsch bd99ab3992 corrected parent path im poms 2021-11-15 15:14:04 +01:00
Eyck Jentzsch 66f365d38d externalize string in HelpDialog class 2021-11-15 15:14:04 +01:00
Eyck Jentzsch 59987f262d update key bindings 2021-11-15 15:14:04 +01:00
Eyck Jentzsch 452a28362e fix help link 2021-11-15 15:14:04 +01:00
Eyck Jentzsch d6805f383b add help dialog 2021-11-15 15:14:04 +01:00
Eyck Jentzsch 076611eec7 allow multiple loading of same format 2021-11-15 15:14:04 +01:00
Eyck Jentzsch e0fa55e2c0 corrected parent path im poms 2021-11-15 14:32:55 +01:00
Eyck Jentzsch 9ea1994228 update key bindings 2021-11-15 14:16:46 +01:00
Eyck Jentzsch 36f628c365 add help dialog 2021-11-15 13:07:58 +01:00
Eyck Jentzsch ff87e72510 allow multiple loading of same format 2021-11-15 10:52:59 +01:00
Eyck Jentzsch aef1e29a53 Merge branch 'release/2.13.2' into develop 2021-07-11 14:22:41 +02:00
Eyck Jentzsch 1ebf9ba382 update version numbers 2021-07-11 13:47:48 +02:00
Eyck Jentzsch 929408d08c fix missing gzip txlog file filter in file dialog 2021-07-11 13:47:27 +02:00
Eyck Jentzsch f57fb93525 fix reload issues 2021-07-11 13:46:35 +02:00
Eyck Jentzsch 788065e456 Merge branch 'release/2.13.1' 2021-03-25 21:09:49 +00:00
Eyck Jentzsch 22b46e0525 fix fit zoom level calculation 2021-03-25 21:07:05 +00:00
Eyck Jentzsch b75018239a Merge branch 'master' into develop
Conflicts:
	features/com.minres.scviewer.database.feature/pom.xml
	features/com.minres.scviewer.e4.feature/pom.xml
	features/com.minres.scviewer.e4.platform.feature/pom.xml
	features/com.minres.scviewer.feature/pom.xml
	features/com.minres.scviewer.ui.feature/pom.xml
	plugins/com.minres.scviewer.database.sqlite/pom.xml
	plugins/com.minres.scviewer.database.text/pom.xml
	plugins/com.minres.scviewer.database.ui.swt/pom.xml
	plugins/com.minres.scviewer.database.vcd/pom.xml
	plugins/com.minres.scviewer.database/pom.xml
	plugins/com.minres.scviewer.e4.application/META-INF/MANIFEST.MF
	plugins/com.minres.scviewer.e4.application/pom.xml
	plugins/com.minres.scviewer.ui/pom.xml
	pom.xml
	products/com.minres.scviewer.e4.product/pom.xml
	products/com.minres.scviewer.e4.product/scviewer.product
	releng/com.minres.scviewer.target/pom.xml
	releng/com.minres.scviewer.updateSite/pom.xml
	tests/com.minres.scviewer.database.test/pom.xml
2021-03-25 20:46:30 +00:00
Eyck Jentzsch 598bb8eec7 Merge remote-tracking branch 'origin/master' 2021-03-25 20:33:25 +00:00
Eyck Jentzsch 869265fc13 add gzip files to file dialog filter 2021-03-25 20:27:55 +00:00
Eyck Jentzsch 5ad813527a fix CLI handling 2021-03-25 20:27:55 +00:00
Eyck Jentzsch 73f8d3d50a fix full name display of generators 2021-03-25 20:27:55 +00:00
Eyck Jentzsch da1701195d update version numbers 2021-03-25 20:27:55 +00:00
Eyck Jentzsch 9b6334509e update version numbers 2021-03-25 20:27:54 +00:00
Eyck Jentzsch ac4acc34a4 fix warning and left-overs 2021-03-25 20:27:54 +00:00
Eyck Jentzsch b6963f38d6 move interface to primitive types 2021-03-25 20:27:54 +00:00
Eyck Jentzsch 182a036ade some more refactoring 2021-03-25 20:27:53 +00:00
Eyck Jentzsch 97f2182290 adapt painter 2021-03-25 20:27:53 +00:00
Eyck Jentzsch 1986a8c9c3 add explicit event list 2021-03-25 20:27:53 +00:00
Eyck Jentzsch 6905d96329 add TreeMap facade 2021-03-25 20:27:52 +00:00
Eyck Jentzsch 818f786b1d add dispose check in case view is closed while loading db 2021-03-25 20:27:52 +00:00
Eyck Jentzsch 2948c1bd33 update version numbers 2021-03-25 20:22:15 +00:00
Eyck Jentzsch 78faab404c apply UI fixes
* wrong arrow target of transaction
* wrong zomm factor calculation for zoom fit
* streams and waveforms not visible if at root level
2021-03-25 20:18:50 +00:00
Eyck Jentzsch 64b10970a8 add gzip files to file dialog filter 2021-03-02 21:55:52 +01:00
Eyck Jentzsch 6ab8fd232e fix CLI handling 2021-03-02 21:55:52 +01:00
Eyck Jentzsch f337a94112 fix full name display of generators 2021-03-02 21:55:52 +01:00
Eyck Jentzsch 0135631a3e update version numbers 2021-03-02 21:55:52 +01:00
Eyck Jentzsch d0e1e8801f update version numbers 2021-03-02 21:55:52 +01:00
Eyck Jentzsch d1b3a91979 fix warning and left-overs 2021-03-02 21:55:52 +01:00
Eyck Jentzsch 45c1396e0e move interface to primitive types 2021-03-02 21:55:51 +01:00
Eyck Jentzsch b7301733f0 some more refactoring 2021-03-02 21:55:51 +01:00
Eyck Jentzsch 5df91dbaa8 adapt painter 2021-03-02 21:55:51 +01:00
Eyck Jentzsch 68918689e7 add explicit event list 2021-03-02 21:55:51 +01:00
Eyck Jentzsch c41dd646da add TreeMap facade 2021-03-02 21:55:51 +01:00
Eyck Jentzsch a077389b83 add dispose check in case view is closed while loading db 2021-03-02 21:55:51 +01:00
Eyck Jentzsch caa37375c0 add gzip files to file dialog filter 2021-03-02 21:42:54 +01:00
Eyck Jentzsch 3daea8ab43 fix CLI handling 2021-03-02 19:32:21 +01:00
Eyck Jentzsch c5d77af0d0 fix full name display of generators 2021-03-02 19:32:09 +01:00
Eyck Jentzsch 7f7fdf09f4 update version numbers 2021-02-27 19:13:28 +00:00
Eyck Jentzsch 7aba6a2ecb update version numbers 2021-02-27 19:13:17 +00:00
Eyck Jentzsch 012395b933 Merge branch 'feature/eclipse_collections' into develop 2021-02-27 18:47:31 +00:00
Eyck Jentzsch 787e3accc0 fix warning and left-overs 2021-02-27 14:00:29 +00:00
Eyck Jentzsch b778940c83 move interface to primitive types 2021-02-27 13:59:00 +00:00
Eyck Jentzsch 71297c4e5a some more refactoring 2021-02-27 13:47:37 +00:00
Eyck Jentzsch 1d2395e00d adapt painter 2021-02-27 13:33:15 +00:00
Eyck Jentzsch b69e1886b9 add explicit event list 2021-02-27 13:27:51 +00:00
Eyck Jentzsch d65803a4b7 add TreeMap facade 2021-02-26 11:57:54 +00:00
Eyck Jentzsch 0e49a68e09 add dispose check in case view is closed while loading db 2021-02-24 08:46:19 +00:00
Eyck Jentzsch 7af5593fd4 Merge branch 'release/2.12.2' into develop 2021-02-24 07:05:00 +00:00
Eyck Jentzsch 25545dac51 Merge branch 'release/2.12.2' 2021-02-24 07:04:59 +00:00
Eyck Jentzsch df77af64ca update version numbers 2021-02-24 07:04:47 +00:00
Eyck Jentzsch e264ab2cbe fix name display in waveform view (#6) 2021-02-24 06:56:28 +00:00
Eyck Jentzsch 8c17ed4146 Merge branch 'release/2.12.1' into develop 2021-02-18 08:19:17 +00:00
Eyck Jentzsch 4ee2e8bc68 Merge branch 'release/2.12.1' 2021-02-18 08:19:16 +00:00
Eyck Jentzsch bdcba613d5 fix version numbers 2021-02-18 08:18:47 +00:00
Eyck Jentzsch 049de0ddaf add VM arguments for Java > 9 2021-02-18 08:14:12 +00:00
Eyck Jentzsch 6be3f378d4 renmove deprecated newInstance call 2021-02-18 06:32:14 +00:00
Eyck Jentzsch 6d8aa33fc9 update language level 2021-02-18 06:26:42 +00:00
Eyck Jentzsch b37ba9f2f1 Merge branch 'master' into develop 2021-02-17 21:15:33 +00:00
Eyck Jentzsch 799f083a74 fix hierarchy display 2021-02-17 21:14:28 +00:00
Eyck Jentzsch 156abe63cd fix display bug 2021-02-17 20:27:58 +00:00
Eyck Jentzsch d38016a03f fix display bug 2021-02-17 20:24:25 +00:00
Eyck Jentzsch 5604c3ee5e Merge branch 'release/2.12.0' 2021-01-15 16:50:33 +01:00
Eyck Jentzsch 5adeae15a9 change name of generated product artifacts 2021-01-15 15:52:28 +01:00
Eyck Jentzsch bca94ceb9d fix stream row calculation 2021-01-15 15:51:59 +01:00
Eyck Jentzsch a42f086339 set max heap to 2GB by default 2021-01-14 23:56:04 +01:00
Eyck Jentzsch 888edf32be remove ITxGenerator interface 2021-01-14 23:55:47 +01:00
Eyck Jentzsch ee5536f1b5 update version numbers 2021-01-14 23:51:18 +01:00
Eyck Jentzsch 59859e5d90 cache some more infos for stream 2021-01-14 23:36:29 +01:00
Eyck Jentzsch 5bae086712 fix (Sonarlint) warnings 2021-01-14 23:36:07 +01:00
Eyck Jentzsch 16de83616a adapt ui to database API 2021-01-14 23:14:22 +01:00
Eyck Jentzsch 6530362b89 refactor tests 2021-01-14 23:14:05 +01:00
Eyck Jentzsch 0372e03abb minor database API change 2021-01-14 23:13:11 +01:00
Eyck Jentzsch 26e8ea8a51 change design browser icons 2021-01-14 21:57:27 +01:00
Eyck Jentzsch efd042dd22 fix duplicate version numbers 2021-01-12 20:28:51 +01:00
Eyck Jentzsch d06642d51f Merge branch 'release/2.11.2' into develop 2021-01-12 20:25:46 +01:00
Eyck Jentzsch f27bcd7109 Merge branch 'release/2.11.2' 2021-01-12 20:25:45 +01:00
Eyck Jentzsch eaea5a4653 update version numbers 2021-01-12 20:25:34 +01:00
Eyck Jentzsch fa4bd0ddd5 move database to hashmap 2021-01-12 11:46:21 +01:00
Eyck Jentzsch 3cbbdbee92 reduce default JVM heap size 2021-01-12 11:30:59 +01:00
Eyck Jentzsch d674e155e1 fix wrong name extraction 2021-01-12 11:30:37 +01:00
Eyck Jentzsch cb3366a559 fix project setup 2021-01-12 07:36:41 +01:00
Eyck Jentzsch 8de0e11226 Merge branch 'release/2.11.1' 2021-01-12 07:36:02 +01:00
Eyck Jentzsch b12232329b Merge branch 'release/2.11.1' into develop 2021-01-12 07:36:02 +01:00
Eyck Jentzsch 5e63972500 update version numbers 2021-01-12 07:34:36 +01:00
Eyck Jentzsch d53db967b3 Merge branch 'hotfix/tx_end_time_fix' into develop 2021-01-12 07:18:25 +01:00
Eyck Jentzsch 25170d0d83 Merge branch 'hotfix/tx_end_time_fix' 2021-01-12 07:18:24 +01:00
Eyck Jentzsch ba6446378a store complete transactions
only complete ScvTx should be put into MapDB otherwise later property
updates might get lost. Since they might not end in the order they start
an additional redirection is need to insert with ascending keys
2021-01-12 07:16:42 +01:00
Eyck Jentzsch 12b08ca8c6 fix version numbers to adhere to eclipse versioning 2021-01-10 16:26:46 +01:00
Eyck Jentzsch bb60f3a9c8 fix bintray repo layout 2021-01-10 15:27:40 +01:00
Eyck Jentzsch ba347a258d refactor structure 2021-01-10 14:40:25 +01:00
Eyck Jentzsch ac41feff6e cleanup 2021-01-10 14:34:12 +01:00
Eyck Jentzsch ec9eb17ee3 Merge branch 'release/2.11.0' 2021-01-10 13:37:39 +01:00
Eyck Jentzsch d2cf13d64b Merge branch 'release/2.11.0' into develop 2021-01-10 13:37:39 +01:00
Eyck Jentzsch 7775f5978b update product version numbers in pom and reduce feature product size 2021-01-10 13:37:16 +01:00
Eyck Jentzsch 8c031e7649 add maven launch configs 2021-01-10 13:16:28 +01:00
Eyck Jentzsch 6b2eae7f26 fix repository build and build numbers 2021-01-10 13:09:45 +01:00
Eyck Jentzsch 69805a423f add bintray upload of update site 2021-01-10 02:16:41 +01:00
Eyck Jentzsch f1178157c8 remove left-over test 2021-01-09 23:27:36 +01:00
Eyck Jentzsch 1ebc6eb03a rename interfaces 2021-01-09 23:25:31 +01:00
Eyck Jentzsch 916215c0c2 fix sonarlint messages 2021-01-09 23:24:00 +01:00
Eyck Jentzsch 6c8e149be2 fix unused import 2021-01-09 22:30:20 +01:00
Eyck Jentzsch bd08b69747 Merge branch 'feature/dynamic_stream_addition' into develop 2021-01-09 22:28:42 +01:00
Eyck Jentzsch 33e0061147 update version numbers 2021-01-09 22:27:59 +01:00
Eyck Jentzsch 45e1031e5e fix Sonarlint warnings 2021-01-09 22:23:38 +01:00
Eyck Jentzsch 73b21cb80b add dynamic update of waveform list 2021-01-09 20:10:58 +01:00
Eyck Jentzsch 15fb825548 Merge branch 'feature/generater_as_stream' into develop 2021-01-09 15:43:09 +01:00
Eyck Jentzsch 0d448368cc fix license statement and Sonarlint 2021-01-09 14:26:49 +01:00
Eyck Jentzsch abacae42d7 fix selection handling in viewer and Sonarlint warnings 2021-01-09 13:43:11 +01:00
Eyck Jentzsch 9d2e2e7f64 show children of streams 2021-01-09 12:56:14 +01:00
Eyck Jentzsch eb64cc60c5 fix copyright header, javadoc, and warnings 2021-01-09 12:44:15 +01:00
Eyck Jentzsch 5ea0d6446a Merge branch 'feature/factor_out_groovy' into develop 2021-01-09 10:36:25 +01:00
Eyck Jentzsch d970d07048 make ITxGenerator as stream 2021-01-09 10:34:22 +01:00
Eyck Jentzsch 90f45c698f fix Sonarlint warnings 2021-01-08 20:50:24 +01:00
Eyck Jentzsch af388b2462 add TX attribute value caching 2021-01-08 20:31:24 +01:00
Eyck Jentzsch 8700e2fdde Improve loading performance 2021-01-08 15:05:24 +01:00
Eyck Jentzsch 23ad84ef30 pure memory based implementation of text db reader 2021-01-07 17:38:29 +01:00
Eyck Jentzsch 0d074ea6ae fix track and relation handling 2021-01-04 17:39:11 +01:00
Eyck Jentzsch 351a246238 separate RelationTypeFactory from RelationType 2021-01-04 17:36:09 +01:00
Eyck Jentzsch 841b5fe2b2 cleanup build 2021-01-03 17:44:52 +01:00
Eyck Jentzsch 85ce07f11f refactor internal transaction representation 2021-01-03 14:16:56 +01:00
Eyck Jentzsch b44f3418f4 fix max concurrency handling 2021-01-02 18:04:48 +01:00
Eyck Jentzsch bfbc40c282 fix serialization issue 2021-01-02 17:02:05 +01:00
Eyck Jentzsch 2c66bf3edd Merge branch 'feature/database_refactor' into develop 2021-01-02 16:31:33 +01:00
Eyck Jentzsch a51f922ea4 refactor groovy classes to Java, 1st step 2021-01-02 16:15:27 +01:00
Eyck Jentzsch 4d8e7a3cd1 fix CLI invocation (at least on MacOS) 2020-11-29 14:06:33 +01:00
Eyck Jentzsch 70ccf35fe4 fix sonar lint reported issues in UI plugins 2020-11-29 12:44:28 +01:00
Eyck Jentzsch 6c5032da10 fix sonarlint issues, add IDerivedWaveform interface 2020-11-29 12:42:26 +01:00
Eyck Jentzsch b2e269b67c update version numbers 2020-11-29 09:52:20 +01:00
Eyck Jentzsch cd01c6a808 fix layout quirks 2020-11-28 20:53:26 +01:00
Eyck Jentzsch 179d40ac8d add track height preferences 2020-11-28 20:44:33 +01:00
Eyck Jentzsch 754a61e989 move color settings into style provider 2020-11-28 20:32:11 +01:00
Eyck Jentzsch bedf4c5c4d move settings into style provider 2020-11-28 19:41:00 +01:00
Eyck Jentzsch 7c27bcec47 moved TX interfaces into own package 2020-11-28 14:47:43 +01:00
Eyck Jentzsch 90f09cc222 adapt UI 2020-11-28 14:08:34 +01:00
Eyck Jentzsch 21d83f93dc refactor database class hierarchy 2020-11-28 10:22:22 +01:00
Eyck Jentzsch db838efc65 Merge branch 'release/2.10.0' into develop 2020-11-27 09:34:21 +01:00
Eyck Jentzsch 929af41dd3 Merge branch 'release/2.10.0' 2020-11-27 09:34:20 +01:00
Eyck Jentzsch 979a7672e6 update version numbers 2020-11-27 09:34:12 +01:00
Eyck Jentzsch 67818ff4dd Squashed commit of the following:
commit dccd2c524c1c00ab3998546276b7244e6d0f389c
Author: eyck <eyck@minres.com>
Date:   Fri Nov 27 08:52:58 2020 +0100

	re-layout sashes

commit d932367a67e081307c49fd248c54c057357a96d2
Author: eyck <eyck@minres.com>
Date:   Fri Nov 27 08:47:50 2020 +0100

	fix string filter and layout

commit 7f4bf0a64d4d685390e9a7af61f377a512d5332d
Author: eyck <eyck@minres.com>
Date:   Fri Nov 27 08:46:30 2020 +0100

	set single clock selection

commit 972e3ce9267c9cd2b9437e5f7403b3ff69d2928f
Author: eyck <eyck@minres.com>
Date:   Fri Nov 27 08:45:49 2020 +0100

	apply filter only to attributes

commit 13a25692bd7909bad98169648edabd01872a7ecb
Author: eyck <eyck@minres.com>
Date:   Fri Nov 27 08:45:22 2020 +0100

	re-layout elements
2020-11-27 08:53:34 +01:00
Eyck Jentzsch 0c7631e912 Merge branch 'release/2.9.3' 2020-11-24 16:20:18 +01:00
Eyck Jentzsch a06b888365 Merge branch 'release/2.9.3' into develop 2020-11-24 16:20:18 +01:00
Eyck Jentzsch ded10e1c22 fix some bugs
* NPE when stream contains no transactions and hence no attributes
* changed sash hierarchy in waveform pane
2020-11-24 16:20:08 +01:00
Eyck Jentzsch 2f3bc0d892 Merge branch 'release/2.9.2' into develop 2020-11-11 18:00:59 +01:00
Eyck Jentzsch 184f1b2d2c Merge branch 'release/2.9.2' 2020-11-11 18:00:58 +01:00
Eyck Jentzsch 90249dadb9 replace TreeViewer with TableViewer in TransactionList 2020-11-11 17:55:10 +01:00
Eyck Jentzsch c88eb32db8 Merge branch 'release/2.9.1' into develop 2020-11-11 07:25:06 +01:00
Eyck Jentzsch d47db29b93 Merge branch 'release/2.9.1' 2020-11-11 07:25:05 +01:00
Eyck Jentzsch e8148af10f update version numbers 2020-11-11 07:24:54 +01:00
Eyck Jentzsch b298a4ca6c fix TX search list implementation 2020-11-10 21:02:34 +01:00
Eyck Jentzsch f474e38687 add type for TransactionTreeNode 2020-11-04 08:50:04 +01:00
Eyck Jentzsch 2e806f7b86 Merge branch 'release/2.9.0' into develop 2020-11-01 18:29:44 +01:00
Eyck Jentzsch b7a8d99e2d Merge branch 'release/2.9.0' 2020-11-01 18:29:43 +01:00
Eyck Jentzsch 2a09fc7e6f update version numbers 2020-11-01 18:29:29 +01:00
Eyck Jentzsch e36abb8d55 Merge branch 'feature/tx_search' into develop 2020-11-01 18:18:21 +01:00
Eyck Jentzsch 5746b9f758 add initial version of search list 2020-11-01 17:42:16 +01:00
Eyck Jentzsch 6be1f018b1 add search dialog and pane 2020-10-29 07:48:35 +01:00
Eyck Jentzsch 38a046aaef Merge branch 'release/2.8.1' into develop 2020-10-28 14:19:51 +01:00
Eyck Jentzsch fdbd6f8402 Merge branch 'release/2.8.1' 2020-10-28 14:19:50 +01:00
Eyck Jentzsch 1dad204494 update release numbers 2020-10-28 14:19:25 +01:00
Eyck Jentzsch 54661c51ce Merge branch 'release/2.8.1' 2020-10-28 14:16:13 +01:00
Eyck Jentzsch b34fe0d0ba add reload menu and toolbar item 2020-10-28 13:33:07 +01:00
Eyck Jentzsch 868eceddc5 fix CLI handling 2020-10-28 13:33:06 +01:00
Eyck Jentzsch b642c396c7 add reload menu and toolbar item 2020-10-28 13:32:17 +01:00
Eyck Jentzsch f3d8e34ed0 fix CLI handling 2020-10-27 18:11:03 +01:00
Eyck Jentzsch 401c760e01 Merge branch 'release/2.8' into develop 2020-10-19 07:09:27 +02:00
Eyck Jentzsch 5add0e385d Merge branch 'release/2.8' 2020-10-19 07:09:27 +02:00
Eyck Jentzsch 2ba6822f2b update version numbers 2020-10-19 07:09:11 +02:00
Eyck Jentzsch 54124d13bf fix trace add problem when only a track is selected 2020-10-19 07:09:00 +02:00
Eyck Jentzsch 5b949a9d37 fix packaging name 2020-10-18 17:48:26 +02:00
Eyck Jentzsch 9a9b25622c fix arrow painter if stream is moved 2020-10-17 13:54:34 +02:00
Eyck Jentzsch dd6a8669b1 fix arrow paint error 2020-10-17 13:13:34 +02:00
Eyck Jentzsch b483897769 Merge branch 'feature/unified_window' into develop 2020-10-17 12:46:19 +02:00
Eyck Jentzsch bd24c4dd05 update JDT/Groovy settings 2020-10-17 12:44:55 +02:00
Eyck Jentzsch 664e99d09e re-organize layout and fix drawing errors 2020-10-17 12:44:33 +02:00
Eyck Jentzsch 675b2ed972 Merge branch 'develop' into feature/unified_window 2020-10-04 18:33:37 +02:00
Eyck Jentzsch e6f19311b1 re-arrange directory structure 2020-10-04 18:30:18 +02:00
Eyck Jentzsch 4a8034191c Merge branch 'develop' 2020-10-04 18:13:36 +02:00
Eyck Jentzsch 9d4e5bf636 fix product setup 2020-10-04 18:12:50 +02:00
Eyck Jentzsch cc9be70708 refactor to integrate design browser 2020-10-04 16:20:16 +02:00
Eyck Jentzsch 2dcc72f679 Merge branch 'release/2.7.1' 2020-07-26 15:27:31 +02:00
Eyck Jentzsch 563b6e2b01 Merge branch 'release/2.7.1' into develop 2020-07-26 15:27:31 +02:00
Eyck Jentzsch 6161c0e0ec update version numbers 2020-07-26 15:16:55 +02:00
Eyck Jentzsch 61c2102ab7 fix ITx selection issue 2020-07-26 15:07:55 +02:00
Eyck Jentzsch 796a04b981 fix tree node issue 2020-07-26 14:57:09 +02:00
Eyck Jentzsch 2a71ab94f5 add plugin based product 2020-07-26 14:41:36 +02:00
Eyck Jentzsch 923b89c038 add eclipse IDE compatibility fix 2020-07-22 21:15:50 +02:00
Eyck Jentzsch 3de6c9900c Merge branch 'release/2.7.0' 2020-07-15 22:08:05 +02:00
Eyck Jentzsch ef1f2a758a fix wrongly shown signals and streams in table 2020-07-15 21:58:49 +02:00
Eyck Jentzsch 3e6d5bd33e update version numbers 2020-07-15 21:48:41 +02:00
Eyck Jentzsch 26968b8521 add multi-selection in waveform viewer 2020-07-15 21:43:07 +02:00
Eyck Jentzsch 611cfc7b46 change package structure 2020-07-15 21:42:10 +02:00
Eyck Jentzsch 8cb77a555b refactor plugin structures 2020-07-15 21:39:30 +02:00
Eyck Jentzsch b4a7f032f5 update versions 2020-07-11 21:05:04 +02:00
Eyck Jentzsch 62e0ec2008 Merge branch 'release/v2.6.2' 2020-07-11 19:57:17 +02:00
Eyck Jentzsch f6cc12e8c3 fix analog painting calculation issue 2020-07-11 19:36:45 +02:00
Eyck Jentzsch b38b268fcb refactor class names 2020-07-11 19:36:28 +02:00
Eyck Jentzsch 55a14edc9d improve preferences handling and textual representation 2020-06-21 17:06:18 +02:00
Eyck Jentzsch 45c23564b5 Merge branch 'release/2.6.1' 2020-06-21 16:04:29 +02:00
Eyck Jentzsch b170fb3c2a Merge branch 'release/2.6.1' into develop 2020-06-21 16:04:29 +02:00
Eyck Jentzsch ab975eadd1 udate version numbers 2020-06-21 16:04:22 +02:00
Eyck Jentzsch 7ad70411f3 fix preference handling 2020-06-21 15:37:38 +02:00
Eyck Jentzsch 24720340be update feature definition 2020-06-21 12:23:34 +02:00
Eyck Jentzsch ad51066f1c add tycho settings 2020-06-21 09:25:24 +02:00
Eyck Jentzsch 95e2db60b6 cleanup build and imports 2020-06-20 18:04:57 +02:00
Eyck Jentzsch db1d377da6 fix redraw issues 2020-06-20 17:58:26 +02:00
Eyck Jentzsch 230e8dcc7a update status bar handling 2020-06-14 21:59:42 +02:00
Eyck Jentzsch ab2146102f fix startup 2020-06-14 15:27:27 +02:00
Eyck Jentzsch b1148922d8 implement feature-based product definition 2020-06-11 14:34:14 +02:00
Eyck Jentzsch e4de765da9 Merge branch 'develop' 2020-06-11 11:04:08 +02:00
Eyck Jentzsch 47120c66b7 Merge branch 'feature/feature_base_product' into develop 2020-06-11 10:53:51 +02:00
Eyck Jentzsch 35ccc71f10 fix TransactionDetails handling of attributes 2020-06-11 10:51:59 +02:00
Eyck Jentzsch 688a6abd99 add set of features 2020-06-11 10:49:34 +02:00
Eyck Jentzsch 97693cd0c4 [WIP ]reorganized dir structure 2020-06-01 22:01:00 +02:00
Eyck Jentzsch 3e5ab7b0ac cleanup use of org.equinox.ds 2020-06-01 12:39:20 +02:00
Eyck Jentzsch ad80671dd2 fxi start issues 2020-06-01 12:38:51 +02:00
Eyck Jentzsch 4ab3000b69 fix left-overs 2020-06-01 12:06:33 +02:00
Eyck Jentzsch 572e999a5b remove version constraint on groovy bundle 2020-06-01 11:10:07 +02:00
Eyck Jentzsch e5bcb25d24 update RCP to 2019-12 2020-05-09 14:20:29 +02:00
Eyck Jentzsch 19ea1f0b6d fix #33 2020-05-09 12:52:48 +02:00
Eyck Jentzsch e88014ebba fix reference to org.eclipse.equinox.util 2020-05-09 12:52:48 +02:00
Eyck Jentzsch 10a0608fd3 Revert "update to Eclipse 2020-03 RCP base"
This reverts commit 077a6f8011.
2020-05-09 12:52:47 +02:00
Eyck Jentzsch 60abfb7afd update to Eclipse 2020-03 RCP base 2020-05-09 12:52:47 +02:00
Eyck Jentzsch 6b9e3c0e70 add performance improvement fixes 2020-05-09 12:52:47 +02:00
Eyck Jentzsch 19065cdbb7 remove fixed color assignment 2020-05-09 12:52:46 +02:00
Eyck Jentzsch a7afeb1f7c change database interface 2020-05-09 12:52:44 +02:00
Eyck Jentzsch 150c404cdb fix #33 2020-05-09 12:50:28 +02:00
Eyck Jentzsch 5fe8b261ec fix reference to org.eclipse.equinox.util 2020-05-08 13:22:14 +02:00
Eyck Jentzsch f8a177c9e0 Revert "update to Eclipse 2020-03 RCP base"
This reverts commit 077a6f8011.
2020-05-08 10:39:43 +02:00
Eyck Jentzsch 077a6f8011 update to Eclipse 2020-03 RCP base 2020-05-08 09:53:36 +02:00
Eyck Jentzsch 8b84f1341b add performance improvement fixes 2020-05-08 08:38:43 +02:00
Eyck Jentzsch 20824e75c1 remove fixed color assignment 2020-04-02 14:17:44 +02:00
Eyck Jentzsch fb283ab668 change database interface 2020-04-02 14:17:44 +02:00
Eyck Jentzsch dc3df60716 Merge branch 'release/2.5' into develop 2020-03-30 11:36:07 +02:00
Eyck Jentzsch 47b58ed770 Merge branch 'release/2.5' 2020-03-30 11:36:06 +02:00
Eyck Jentzsch 1cebc2da0a update version numbers 2020-03-30 11:34:26 +02:00
Eyck Jentzsch 414030cbd2 fix behavior of FileBrowserDialog 2020-03-30 11:31:51 +02:00
Eyck Jentzsch 8011585113 add globbing selection and tool bar to file dialog 2020-03-29 17:16:14 +02:00
Eyck Jentzsch 270a004037 fix file detection 2020-03-29 16:10:36 +02:00
Eyck Jentzsch 2aa4160400 Initial version of custom file selection dialog 2020-03-29 16:05:48 +02:00
Eyck Jentzsch c8e416966b Merge branch 'release/2.4.2' into develop 2020-03-27 20:05:55 +01:00
Eyck Jentzsch 20cb208719 Merge branch 'release/2.4.2' 2020-03-27 20:05:54 +01:00
Eyck Jentzsch e30aff82ee set version numbers 2020-03-27 20:05:46 +01:00
Eyck Jentzsch ad2937c332 Merge branch 'master' into develop
Conflicts:
	com.minres.scviewer.e4.application/src/com/minres/scviewer/e4/application/parts/WaveformViewer.java
	com.minres.scviewer.e4.application/src/com/minres/scviewer/e4/application/preferences/DefaultValuesInitializer.java
	com.opcoach.e4.preferences/src/com/opcoach/e4/preferences/internal/E4PreferenceRegistry.java
2020-03-25 23:20:15 +01:00
Eyck Jentzsch 0e8a757d6e fix settings handling
for this a common workspace is being created. To allow for multiple
instances the work space is being unlocked as soon as the application is
initialized
2020-03-25 23:01:28 +01:00
Eyck Jentzsch 220eaea73c move to opcoach repository at https://www.opcoach.com/repository/2018-12 2020-03-25 22:52:39 +01:00
Eyck Jentzsch 24d6085ded update version
this release contains for #31 and #14
2020-03-24 08:09:52 +00:00
Eyck Jentzsch 8a66734279 fix #32 2020-03-24 08:09:52 +00:00
Eyck Jentzsch f6e2224651 fix preferences storage 2020-03-24 08:09:52 +00:00
Eyck Jentzsch 5d85c12bb8 Merge branch 'release/2.4.1' into develop 2020-03-24 08:06:10 +00:00
Eyck Jentzsch 2b1cb7856f update version
this release contains for #31 and #14
2020-03-24 08:05:55 +00:00
Eyck Jentzsch f1d080983a fix #32 2020-03-24 06:43:10 +00:00
Eyck Jentzsch 767b083a22 fix preferences storage 2020-03-21 15:17:34 +01:00
Eyck Jentzsch 348ffe20d6 Merge branch 'release/2.4.0' into develop 2020-03-21 11:35:52 +01:00
Eyck Jentzsch f3494e7562 Merge branch 'release/2.4.0' 2020-03-21 11:35:51 +01:00
Eyck Jentzsch 9b6908f193 update product version and removed leveldb 2020-03-21 11:35:29 +01:00
Eyck Jentzsch 0cfcc8c118 fix identification of SQLite file 2020-03-21 11:31:59 +01:00
Eyck Jentzsch ec5cd209c6 Merge branch 'feature/add_hover_to_transaction' into develop 2020-03-21 11:31:16 +01:00
Eyck Jentzsch 5933bcd512 configure the hover visibility from UI 2020-03-21 11:30:30 +01:00
Eyck Jentzsch 14ac6654b9 refactor ToolTipHandler to allow more control over content 2020-03-21 08:42:35 +01:00
Eyck Jentzsch cea11743eb clean up of ToolTip handler 2020-03-21 07:31:14 +01:00
Eyck Jentzsch 210d925987 add initial implementation of TX hover 2020-03-21 06:28:28 +01:00
Eyck Jentzsch ffbffd88f1 fix typos 2020-03-17 13:42:18 +01:00
Eyck Jentzsch ec8d54dca3 add multi file feature 2020-03-17 12:54:38 +01:00
Eyck Jentzsch e8682e446b Merge branch 'release/2.2.0' 2020-03-14 15:56:36 +01:00
Eyck Jentzsch e87e1791c3 fix drag-zoom visual 2020-03-14 15:31:51 +01:00
Eyck Jentzsch f5a4e3c39e cleanup of code 2020-03-13 22:26:53 +01:00
Eyck Jentzsch 306e20d14f fix compiler warnings 2020-03-13 22:25:42 +01:00
Eyck Jentzsch c76805d133 Merge branch 'release/2.2.0' into develop 2020-03-13 22:12:10 +01:00
Eyck Jentzsch 8ebb2dc3a6 Merge branch 'release/2.2.0' 2020-03-13 22:12:09 +01:00
Eyck Jentzsch 669fd6a881 update version numbers 2020-03-13 22:11:39 +01:00
Eyck Jentzsch 8e66e5fe0b add a select-all key stroke to design browsers stream table 2020-03-13 22:06:55 +01:00
Eyck Jentzsch 674aa7d3ce add initial version of drag-zoom 2020-03-13 21:06:53 +01:00
Eyck Jentzsch 2b7c9a240f add drag area to wavefrom view 2020-03-13 19:52:55 +01:00
Eyck Jentzsch 18f2a110f0 add shortcuts for zoom 2020-03-13 18:51:35 +01:00
Eyck Jentzsch 27f1e8ca6a Merge branch 'hotfix/#28_add_filter' 2020-03-13 16:03:22 +01:00
Eyck Jentzsch 3565a6a05b Merge branch 'hotfix/#28_add_filter' into develop 2020-03-13 16:03:22 +01:00
Eyck Jentzsch aef4901b2b bug fix for #28
the filter for the treeviewer did not work properly and needed to be
re-implemented
2020-03-13 16:03:16 +01:00
Eyck Jentzsch d77aaa3865 Merge branch 'hotfix/#29_cannot_delete_signals' into develop 2020-03-13 16:01:30 +01:00
Eyck Jentzsch b398a07fd7 Merge branch 'hotfix/#29_cannot_delete_signals' 2020-03-13 16:01:29 +01:00
Eyck Jentzsch 732bd82034 fix #29 cannot delete VCD signals
comparison of objects were done by idendity not .equals()
2020-03-13 16:01:16 +01:00
Eyck Jentzsch 2ccc5274aa Merge branch 'hotfix/#27_add_filter' into develop 2020-03-12 21:59:01 +01:00
Eyck Jentzsch 9f4f71046d Merge branch 'hotfix/#27_add_filter' 2020-03-12 21:59:01 +01:00
Eyck Jentzsch d73b01f77d fix #27 'Add filter to hierarchy viewer' 2020-03-12 18:17:47 +01:00
Eyck Jentzsch 77302521b2 add another fix for #27 'parser doesn't handle truncated input files' 2020-03-11 23:11:42 +01:00
Eyck Jentzsch f89471e14b Merge branch 'release/2.1.6' 2020-03-11 07:51:54 +01:00
Eyck Jentzsch 0634556256 Merge branch 'release/2.1.6' into develop 2020-03-11 07:51:54 +01:00
Eyck Jentzsch 4f097e7f59 update version numbers 2020-03-11 07:51:09 +01:00
Eyck Jentzsch f1abc645c3 Merge branch 'hotfix/#27_truncated_input' 2020-03-11 07:37:21 +01:00
Eyck Jentzsch 2c8b7129e9 add fix for #27 'parser doesn't handle truncated input files' 2020-03-11 07:37:14 +01:00
Eyck Jentzsch 2f81529f93 Merge branch 'hotfix/#26_incomplete_regex_throws_exception' into develop 2020-03-11 07:35:43 +01:00
Eyck Jentzsch 8f76d816f1 Merge branch 'hotfix/#26_incomplete_regex_throws_exception' 2020-03-11 07:35:42 +01:00
Eyck Jentzsch 594a71a9ba add try-catch to filter application 2020-03-11 07:35:31 +01:00
Eyck Jentzsch 0fa0d4a5b8 Merge branch 'hotfix/#27_truncated_input' into develop 2020-03-11 07:27:16 +01:00
Eyck Jentzsch 97443c6cd8 Merge branch 'release/2.1.4' 2020-03-09 18:02:19 +01:00
Eyck Jentzsch c3486eb6c8 Updated version numbers 2020-03-09 18:00:15 +01:00
Eyck Jentzsch 8a286a8eac Merge branch 'release/2.1.4' 2020-03-09 17:58:47 +01:00
Eyck Jentzsch e4329213fa Fixed missing version number 2020-03-09 17:55:50 +01:00
Eyck Jentzsch 857bbe17d7 Merge branch 'release/2.1.3' 2020-03-09 17:55:05 +01:00
Eyck Jentzsch 091cb1cf7f Fixed missing version number 2020-03-09 17:53:35 +01:00
Brita Keller 0623b8de4a Fixed #25 - TransactionDetails vanish when setting filter for streamlist 2020-02-28 21:19:40 +01:00
Brita Keller 8b55548cca Fixed #16 - Setting and deleting filter removes properties 2020-02-17 19:43:27 +01:00
Eyck Jentzsch a0a4ea1f53 Merge branch 'release/2.1.3'
Conflicts:
	com.minres.scviewer.e4.application/META-INF/MANIFEST.MF
2020-02-10 07:04:54 +01:00
Eyck Jentzsch e034d1faad Updated version numbers 2020-02-10 07:03:24 +01:00
Eyck Jentzsch 68660d0892 Merge branch 'release/2.1.2' 2020-02-10 06:50:07 +01:00
Eyck Jentzsch a0f0b55045 Updated application bundle version number 2020-02-10 06:46:55 +01:00
Eyck Jentzsch 3d20b6961e Updated groovy compiler level 2020-02-10 02:25:40 +01:00
Brita Keller ca02c92a18 Fixed #23 - Fix sync of properties 2020-02-03 21:30:44 +01:00
Eyck Jentzsch 347dbf134b removed javax.xml.bind dependency 2020-01-28 23:23:15 +01:00
Brita Keller 44ac32359f Fixed #19 - Fixed sync of streamlist 2020-01-27 20:37:53 +01:00
Eyck Jentzsch 48c7d81b8a Merge branch 'develop' of VP-Tools/SCViewer into master 2020-01-13 12:26:56 +00:00
Eyck Jentzsch e945a3acbe Fixed NP and updated build infrastructure and target platform 2020-01-13 12:26:56 +00:00
Eyck Jentzsch 145dfbd74d Fixed NP and updated build infrastructure and target platform 2020-01-13 12:26:56 +00:00
Eyck Jentzsch b7646dc29e Extended data type for text database 2020-01-13 12:26:56 +00:00
Brita Keller 475181c8df Fixed #21 - IllegalStateException when closing SCViewer 2020-01-13 12:26:56 +00:00
Brita Keller 3d043ce1b7 Fixed #20 - NPE gets caught when opening a damaged file and info popup
is shown
2020-01-13 12:26:56 +00:00
Brita Keller 7e2869b7ee Fixed # 18 - Fixed tree view and stream list after last tab is closed 2020-01-13 12:26:56 +00:00
Eyck Jentzsch 51d9d7e25c Fixed NP and updated build infrastructure and target platform 2020-01-13 13:17:27 +01:00
Eyck Jentzsch ed665ac032 Fixed NP and updated build infrastructure and target platform 2020-01-13 13:14:34 +01:00
Eyck Jentzsch 689d7874f4 Extended data type for text database 2020-01-07 12:43:39 +01:00
Brita Keller c7c1f97b0c Fixed #21 - IllegalStateException when closing SCViewer 2019-12-18 17:16:20 +01:00
Brita Keller 7b2db09585 Fixed #20 - NPE gets caught when opening a damaged file and info popup
is shown
2019-12-03 22:19:56 +01:00
Brita Keller 52f3f7b348 Fixed # 18 - Fixed tree view and stream list after last tab is closed 2019-11-25 17:46:28 +01:00
Eyck Jentzsch 429ea37080 Merge branch 'release/2.1.2' 2019-09-11 13:08:52 +02:00
Eyck Jentzsch 04f011e82f Fixed array out of bounds error 2019-09-11 13:05:26 +02:00
Brita Keller 3a6ed3ce7e Fixed 'Save Settings' #4 2019-09-04 20:50:34 +02:00
Eyck Jentzsch 79eb8073d8 Merge branch 'release/2.1.1' 2019-07-08 18:44:47 +02:00
Eyck Jentzsch 4ea841cc7b Fixed Part Stack issue and updated About Dialog 2019-07-08 18:38:53 +02:00
Eyck Jentzsch 6bf642d2af Merge branch 'release/2.1.0' 2019-06-30 11:54:10 +02:00
Brita Keller 15c24f0925 Fixed #15 - no more ArrayOutOfBounds exception when deleting last stream 2019-06-25 22:21:10 +02:00
Brita Keller 4d4a6579c6 Fixed #13 - added transaction colors 2019-06-09 21:49:45 +02:00
Eyck Jentzsch b648dccfd8 Fixed NPE in case of detached waveform 2019-04-11 06:24:47 +00:00
Eyck Jentzsch f9425834a3 Merge branch 'develop' of https://eyck@git.minres.com/VP/SCViewer.git into develop 2019-04-11 06:09:46 +00:00
Eyck Jentzsch e0d00e6402 Settings update 2019-04-11 06:09:35 +00:00
Eyck Jentzsch b1b7517759 Merge branch 'release/2.0.5' into develop 2019-03-19 19:15:28 +01:00
Eyck Jentzsch a5b9c28007 Merge branch 'release/2.0.5' 2019-03-19 19:15:27 +01:00
Eyck Jentzsch b5e408595b Updated version numbers 2019-03-19 19:14:39 +01:00
Eyck Jentzsch f15930682e Merge branch 'develop' of https://git.minres.com/VP/SCViewer.git into develop 2019-03-19 18:46:56 +01:00
Brita Keller f8765518bc Fixed #10 -all status bar parts now visible after starting program 2019-03-18 17:17:18 +01:00
Brita Keller 73e952faea Fixed #11 - No more display problems in waveforms when zooming in and
scrolling
2019-03-18 17:17:17 +01:00
Eyck Jentzsch 111383bcba Merge branch 'release/2.0.4' into develop 2019-03-15 08:53:16 +01:00
Eyck Jentzsch 244f005ae6 Merge branch 'release/2.0.4' 2019-03-15 08:53:15 +01:00
Eyck Jentzsch 701733e69d Updated version numbers and display in about dialog 2019-03-15 08:50:12 +01:00
Eyck Jentzsch 6b4a9c1e14 Fixed handling of TransactionDetails view state 2019-03-15 07:31:13 +00:00
Eyck Jentzsch 3abbc3e0e2 Merge branch 'master' into develop 2019-03-14 21:29:23 +01:00
Eyck Jentzsch 2a7b713ef2 Merge branch 'release/2.0.3' 2019-03-14 21:26:55 +01:00
Eyck Jentzsch 593571ce10 Merge branch 'hotfix/selction_bug' into develop 2019-03-14 21:23:59 +01:00
Eyck Jentzsch 979261432a Merge branch 'hotfix/selction_bug' 2019-03-14 21:23:58 +01:00
Eyck Jentzsch e25f56a8a9 Fixed NPE 2019-03-14 21:23:39 +01:00
Eyck Jentzsch e8788e6ce0 Merge branch 'master' into develop 2019-03-14 21:14:26 +01:00
Eyck Jentzsch 8a8691a889 Merge branch 'release/2.0.2' 2019-03-14 21:11:51 +01:00
Eyck Jentzsch de15b84bef Merge branch 'feature/keep_properties_scroll' into develop 2019-03-14 20:51:31 +01:00
Eyck Jentzsch b78d8bea45 Fixed format detection bug and adapted VCD loader to Arteris specifics 2019-03-14 20:51:02 +01:00
Eyck Jentzsch a49842a119 Added logic to keep the same element revealed in transaction details
view
2019-03-14 19:28:38 +01:00
Eyck Jentzsch dbe5d603ed Fixed EOFException so that partial gzip files get loaded 2019-03-14 19:27:58 +01:00
Eyck Jentzsch cde1835c74 Merge branch 'develop' of https://eyck@git.minres.com/VP/SCViewer.git into develop 2018-11-06 12:36:40 +01:00
Eyck Jentzsch b332eca891 Fixed about dialog handling 2018-11-06 12:36:25 +01:00
Eyck Jentzsch 75efd69b77 Merge branch 'master' of https://github.com/Minres/SCViewer 2018-11-06 12:33:34 +01:00
Eyck Jentzsch 960610bab2 Fixed about dialog handling 2018-11-06 12:28:45 +01:00
Eyck Jentzsch 98029f8c7a Merge branch 'master' of https://eyck@git.minres.com/VP/SCViewer.git 2018-11-06 12:08:42 +01:00
Eyck Jentzsch 9c09cf5f40 Merge branch 'release/2.0.0' into develop 2018-11-06 12:07:37 +01:00
Eyck Jentzsch 113d65cfc4 Merge branch 'release/2.0.0' 2018-11-06 12:07:35 +01:00
Eyck Jentzsch 22e7bb04f0 Updated documentation 2018-11-06 12:07:23 +01:00
Eyck Jentzsch d8c5b7d9c3 Merge branch 'develop' of https://git.minres.com/VP/SCViewer into
develop

# Conflicts:
#	com.minres.scviewer.e4.application/src/com/minres/scviewer/e4/application/parts/WaveformViewer.java
2018-11-06 08:35:00 +01:00
Eyck Jentzsch 2e7ae5309f Merge branch 'feature/MapDB-backing' into develop 2018-11-06 08:32:12 +01:00
Eyck Jentzsch f81e830e93 Updated UI to reflect database changes esp. in VCD 2018-11-06 08:26:39 +01:00
Eyck Jentzsch 47a285bf6d Added MapDB backing store for Text based Transactions, cleanup of
imports etc.
2018-11-06 08:24:26 +01:00
Eyck Jentzsch c080773878 Removed jdbm files and added MapDB 3.0.7 jar files 2018-11-05 18:33:51 +01:00
Eyck Jentzsch bcf4e1a274 Adapted application to data model changes 2018-11-05 18:23:17 +01:00
Eyck Jentzsch 93fd192782 Refactored database data model to improve speed and reduce memory
consumption
2018-11-05 18:22:28 +01:00
Eyck Jentzsch e687eef42c Fixed Tx properties 2018-11-05 18:16:59 +01:00
Eyck Jentzsch 2a709113fe Uodated tests to run on LevelDB backed transaction db 2018-11-03 17:49:40 +01:00
Eyck Jentzsch 8d7acdb9cb Added LevelDB transaction db reader 2018-11-03 17:48:36 +01:00
Eyck Jentzsch 77684d828c Merge branch 'master' of https://git.minres.com/VP/SCViewer 2018-10-15 22:31:22 +02:00
Eyck Jentzsch 9f902057ba Merge branch 'release/2.0.1' 2018-10-15 22:30:02 +02:00
Eyck Jentzsch 42660c7a2e Fixed some warnings 2018-10-15 22:01:44 +02:00
Eyck Jentzsch 97a806f9d9 Changed selection handling and fixed keycode handling 2018-10-15 21:55:35 +02:00
Eyck Jentzsch 60185fbaee Version inconsitency fixes 2018-10-15 10:09:51 +02:00
Eyck Jentzsch 5411a405d4 Merge branch 'master' of https://github.com/Minres/SCViewer 2018-10-15 09:32:43 +02:00
Eyck Jentzsch 694dad1cf5 Merge branch 'develop' 2018-10-15 09:17:44 +02:00
Eyck Jentzsch 5fb9a7dcdf Merge branch 'master' of https://git.minres.com/VP/SCViewer 2018-10-15 09:15:04 +02:00
Eyck Jentzsch 3faa7205ae Merge branch 'release/2.0' into develop 2018-10-15 09:13:55 +02:00
Eyck Jentzsch 3cbe5d56d9 Merge branch 'release/2.0' 2018-10-15 09:13:54 +02:00
Eyck Jentzsch 5745ab4f2c Fixed cli options and NaN handling in VCD loader 2018-10-15 09:13:41 +02:00
Eyck Jentzsch 4a17108ccc Moved commonly used constants to own class 2018-10-15 09:10:10 +02:00
Eyck Jentzsch 408138c27c Merge branch 'develop' of https://git.minres.com/VP/SCViewer into develop 2018-10-15 07:50:31 +02:00
Eyck Jentzsch 3211db8743 Merge branch 'release/2.0' 2018-10-14 21:29:23 +02:00
Eyck Jentzsch 0c81988d8c Merge branch 'release/2.0' into develop 2018-10-14 21:29:23 +02:00
Eyck Jentzsch 0107c423a3 Fixed warnings in Java files and MANIFESTS.MF, updated version numbers 2018-10-14 21:29:09 +02:00
Eyck Jentzsch 5d41816eb6 Merge branch 'feature/add-keyboard-navigation' into develop 2018-10-14 20:44:41 +02:00
Eyck Jentzsch e41b7e25ed Updated reading/writing of scview files, added command line argument to
load config file
2018-10-14 20:44:17 +02:00
Eyck Jentzsch dba8b2731d Added keyboard navigation to scroll, move selection, move selected and
zoom
2018-10-14 17:30:37 +02:00
Eyck Jentzsch 048fa93b53 Merge branch 'feature/Extend-waveform-configurability' into develop 2018-10-14 08:59:37 +02:00
Eyck Jentzsch 6cc5597c25 Added handler and configurability of wavefrom view 2018-10-14 08:59:17 +02:00
Eyck Jentzsch 694423be3b [WIP] extended application to configure waveform display 2018-10-11 15:23:07 +02:00
Eyck Jentzsch 0634b9cfd5 Merge branch 'develop' of https://git.minres.com/VP/SCViewer.git into
develop

Conflicts:
	com.minres.scviewer.database.test/inputs/simple_system.vcd
	com.minres.scviewer.database.test/src/com/minres/scviewer/database/test/DatabaseServicesTest.java
	com.minres.scviewer.database.text/.settings/org.eclipse.jdt.groovy.core.prefs
	com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/swt/internal/SignalPainter.java
	com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/swt/internal/WaveformCanvas.java
	com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/swt/internal/WaveformViewer.java
	com.minres.scviewer.database.ui/src/com/minres/scviewer/database/ui/WaveformColors.java
	com.minres.scviewer.database.vcd/src/com/minres/scviewer/database/vcd/VCDDbLoader.java
	com.minres.scviewer.database.vcd/src/com/minres/scviewer/database/vcd/VCDFileParser.java
	com.minres.scviewer.e4.application/plugin.xml
	com.minres.scviewer.e4.product/scviewer.product
	com.minres.scviewer.target/neon.target
	com.minres.scviewer.target/photon.target
	com.minres.scviewer.ui/.settings/org.eclipse.jdt.groovy.core.prefs
2018-10-11 15:22:10 +02:00
Eyck Jentzsch c4b16338a8 Merge branch 'feature/real_signal_support' into develop 2018-10-11 14:53:07 +02:00
Eyck Jentzsch bda2a84532 Fixed #6 Implement right-click menue for signal names 2018-10-11 14:48:36 +02:00
Eyck Jentzsch 9d4c951e7f Added support for real valued signals in waveform UI 2018-10-11 14:44:41 +02:00
Eyck Jentzsch 8a89b21d34 Added database support to read real valued signals 2018-10-11 11:23:28 +02:00
Eyck Jentzsch e02df858fa Merge branch 'develop' of https://git.minres.com/VP/SCViewer into develop 2018-09-26 21:25:23 +02:00
Eyck Jentzsch 0fbe22e2e0 Fixed NPE when concurrencyLevel is 0 2018-09-26 21:25:02 +02:00
Brita Keller 021c2bceff Fixed ZoomFit and encoding issue 2018-09-17 17:06:57 +02:00
Brita Keller 9862c14040 Additional files missing in the last commit 2018-09-17 16:38:27 +02:00
Brita Keller 23131e7527 Fixed ZoomFit 2018-09-17 16:38:26 +02:00
Eyck Jentzsch 0833bca1bd Fixed VCD parsing error 2018-09-17 16:38:25 +02:00
Eyck Jentzsch 1cf00ec882 Updated to Photon platform 2018-09-17 16:38:24 +02:00
Eyck Jentzsch 6c0bbd5f1f Added hierarchical VCD File 2018-09-17 16:32:07 +02:00
Eyck Jentzsch f13b091664 Update README.md 2018-09-17 16:26:49 +02:00
Eyck Jentzsch dff41eceb2 Updated version numbers 2018-09-17 16:26:49 +02:00
Eyck Jentzsch d267e64907 Fixed parsing bug 2018-09-17 16:26:49 +02:00
Eyck Jentzsch 792dcd55f3 Fixed rendering of integral transaction attributes and updated version
numbers
2018-09-17 16:26:48 +02:00
Eyck Jentzsch 2b33ea0c85 Fixed drawing issue for very long transactions 2018-08-27 23:46:06 +02:00
Eyck Jentzsch f69a6f2e89 Removed jdbm 2018-08-27 23:16:54 +02:00
Eyck Jentzsch 0f133a3df6 Added UI support for real value signals 2018-08-27 23:13:19 +02:00
Eyck Jentzsch 2f11d8ed7b Fixed VCD parsing error 2018-07-16 13:17:58 +02:00
Eyck Jentzsch 7dd0a24df6 Updated to Photon platform 2018-07-16 13:17:43 +02:00
Eyck Jentzsch a052fe2324 Added hierarchical VCD File 2018-07-14 11:42:55 +02:00
Eyck Jentzsch 4fc24453ae Merge branch 'develop'
* develop:
  Update README.md
2018-04-05 23:10:31 +02:00
Eyck Jentzsch b468fba441 Update README.md 2018-04-05 23:04:12 +02:00
Eyck Jentzsch f80021059e
Update README.md 2017-11-01 19:02:01 +01:00
Eyck Jentzsch 7c0822b4d2 Merge pull request #4 from Minres/develop
Develop
2017-10-14 19:06:53 +00:00
Eyck Jentzsch cdde384ed3 Updated version numbers 2017-10-14 20:59:30 +02:00
Eyck Jentzsch fce1bdb1e7 Fixed parsing bug 2017-10-14 20:46:22 +02:00
Eyck Jentzsch b68a7d3259 Merge pull request #3 from Minres/develop
Changes for 1.2.1 version
2017-10-13 08:03:11 +00:00
Eyck Jentzsch 44b13c105d Fixed rendering of integral transaction attributes and updated version
numbers
2017-10-13 09:36:44 +02:00
746 changed files with 48139 additions and 26589 deletions

5
.gitignore vendored
View File

@ -6,5 +6,8 @@
SCViewer initiator_target.launch
SCViewer.xcf
SCViewer_1.png
*.launch
copyrightLog.txt
/workspace
?*.launch
/.settings/
.tycho-consumer-pom.xml

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
<intAttribute key="M2_COLORS" value="0"/>
<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
<stringAttribute key="M2_GOALS" value="package"/>
<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
<booleanAttribute key="M2_OFFLINE" value="false"/>
<stringAttribute key="M2_PROFILES" value=""/>
<listAttribute key="M2_PROPERTIES"/>
<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
<intAttribute key="M2_THREADS" value="1"/>
<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
<stringAttribute key="M2_USER_SETTINGS" value=""/>
<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
</launchConfiguration>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
<intAttribute key="M2_COLORS" value="0"/>
<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
<stringAttribute key="M2_GOALS" value="package"/>
<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
<booleanAttribute key="M2_OFFLINE" value="false"/>
<stringAttribute key="M2_PROFILES" value="release-composite"/>
<listAttribute key="M2_PROPERTIES"/>
<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
<intAttribute key="M2_THREADS" value="4"/>
<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
<stringAttribute key="M2_USER_SETTINGS" value=""/>
<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="true"/>
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
</launchConfiguration>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
<intAttribute key="M2_COLORS" value="0"/>
<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
<stringAttribute key="M2_GOALS" value="tycho-versions:set-version tycho-versions:update-pom"/>
<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
<booleanAttribute key="M2_OFFLINE" value="false"/>
<stringAttribute key="M2_PROFILES" value=""/>
<listAttribute key="M2_PROPERTIES">
<listEntry value="newVersion=${string_prompt:new version number}"/>
</listAttribute>
<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
<intAttribute key="M2_THREADS" value="1"/>
<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
<stringAttribute key="M2_USER_SETTINGS" value=""/>
<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
<stringAttribute key="bad_container_name" value="/com.minres.scviewer.parent/.launch"/>
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
</launchConfiguration>

100
README.md
View File

@ -1,42 +1,70 @@
SCViewer
========
SCViewer is composed of a set of eclipse plugins to display VCD and transaction streams
created by the SystemC VCD trace implementation and the SystemC Verification Library (SCV).
For further description of the SCV please refer to
http://www.accellera.org/activities/committees/systemc-verification.
SCViewer is composed of a set of eclipse plugins to display VCD (e.g. created by SystemC VCD trace) and transaction streams. Those streams can be
created by the SystemC Verification Library (SCV, For further description of the SCV please refer to https://www.accellera.org/activities/working-groups/systemc-verification) or by the **L**ight**w**eight **T**ranasaction **R**ecording for SystemC ( [LWTR4SC](https://github.com/Minres/LWTR4SC) ).
The viewer is in early alpha stage and not yet ready for production use!
The viewer has the following features
- support of VCD files (compressed and uncompressed)
- real numbers
- showing vectors and real numbers as analog (step-wise & continuous)
- various value representations of bit vectors
- support of SCV transaction recordings in various formats
- text log files (compressed and uncompressed)
- sqlite based
- visualization of transaction relations
The plugins are structured as follows:
- com.minres.scviewer.database
the interface defining the API to access the database and the implementation for VCD
- com.minres.scviewer.database.text
an implementation of the API to read the text files generated by the SCV
sc_tr_text database
- com.minres.scviewer.database.sqlite
an implementation of the API to read the files generated by implementation in the
sc_tr_sqlite project using a SQLite based database
- com.minres.scviewer.database.test
a some JUnit tests of the 3 back ends
- com.minres.scviewer.ui
the viewer it self to diplay the transactions and associated views like the
outline of the DB and the properties of the transaction
- com.minres.scviewer.feature
the feature combining the plugins above into a somhow usable form
- scv_tr_sqlite
a C++ project containing the SQLite based SCV database implementation and the scv4tlm
socket implementations.
A simple example (scv_tr_recording_example.cpp) for testig purposes of the database is
provided.
To build the plugins the Eclipse SDK or PDE can be used. In both cases the Groovy
eclipse plugin (http://groovy.codehaus.org/Eclipse+Plugin or Market) has to be
installed.
> If you encounter issue when running on Linux please try running as `SWT_GTK3=0 scviewer` as there exist issues wiht GTK3.
To build the plugins the Eclipse SDK or PDE can be used.
Key Shortcuts
=============
Legend:
* Left Mouse Button: LMB
* Middle Mouse Button: MMB
* Mouse Scroll wheel: MScrl
* Context any means Name List, Value List or Waveform
| Input | Modifier | Context | Action |
|------------|----------|----------|-----------------------------------|
| LMB click | | any | select |
| LMB click | Shift | Waveform | move selected marker to position |
| LMB click | Control | Waveform | move cursor to position |
| LMB drag | | Waveform | zoom to range |
| MMB click | | Waveform | move selected marker to position |
| MScrl | | any | scroll window up/down |
| MScrl | Shift | any | scroll window left/right |
| MScrl | Control | Waveform | zoom in/out |
| Key left | | Waveform | scroll window to the left (slow) |
| Key right | | Waveform | scroll window to the right (slow) |
| Key left | Shift | Waveform | scroll window to the left (fast) |
| Key right | Shift | Waveform | scroll window to the right (fast) |
| Key up | | Waveform | move selection up |
| Key down | | Waveform | move selection down |
| Key up | Control | Waveform | move selected track up |
| Key down | Control | Waveform | move selected track down |
| Key + | Control | Waveform | zoom in |
| Key - | Control | Waveform | zoom out |
| Key Pos1 | | Waveform | jump to selected marker |
| Key End | | Waveform | jump to cursor |
| Key Del | | any | delete selected entries |
| LMB click | | ZoomBar | increment/decrement 1 page |
| LMB drag | | ZoomBar | drag both markers (pan) |
| LMB drag | Control | ZoomBar | drag one marker (zoom) |
| MMB drag | | ZoomBar | drag one marker (zoom) |
| xMB dclick | | ZoomBar | pan to position |
| MScrl | | ZoomBar | scroll window left/right |
| MScrl | Shift | ZoomBar | scroll window left/right double speed |
| MScrl | Control | ZoomBar | zoom in/out |
| Key left | | ZoomBar | scroll window to the left (slow) |
| Key right | | ZoomBar | scroll window to the right (slow) |
| Key up | | ZoomBar | scroll window to the left (slow) |
| Key down | | ZoomBar | scroll window to the right (slow) |
| Key PgUp | | ZoomBar | scroll window to the left (fast) |
| Key PgDown | | ZoomBar | scroll window to the right (fast) |
| Key Pos1 | | ZoomBar | scroll to begin |
| Key End | | ZoomBar | scroll to end |
TODO
====
- add more tests
- move to feature based product to allow automatic updates
- improve graphics
- catch-up e3 plugin to functionality of e4 product

View File

@ -1,7 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,52 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.sqlite;
import java.util.List;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.ITxGenerator;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.sqlite.tables.ScvGenerator;
public class TxGenerator implements ITxGenerator {
private ITxStream<ITxEvent> stream;
private ScvGenerator scvGenerator;
public TxGenerator(ITxStream<ITxEvent> stream, ScvGenerator scvGenerator) {
this.stream=stream;
this.scvGenerator=scvGenerator;
}
@Override
public Long getId() {
return (long) scvGenerator.getId();
}
@Override
public ITxStream<ITxEvent> getStream() {
return stream;
}
@Override
public String getName() {
return scvGenerator.getName();
}
@Override
public List<ITx> getTransactions() {
return null;
}
}

View File

@ -1,199 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.sqlite;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.Vector;
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.ITxGenerator;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformDb;
import com.minres.scviewer.database.IWaveformEvent;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.sqlite.db.IDatabase;
import com.minres.scviewer.database.sqlite.db.SQLiteDatabaseSelectHandler;
import com.minres.scviewer.database.sqlite.tables.ScvGenerator;
import com.minres.scviewer.database.sqlite.tables.ScvStream;
import com.minres.scviewer.database.sqlite.tables.ScvTx;
public class TxStream extends HierNode implements ITxStream<ITxEvent> {
private IDatabase database;
private String fullName;
private IWaveformDb db;
private ScvStream scvStream;
private TreeMap<Integer, TxGenerator> generators;
private TreeMap<Integer, ITx> transactions;
private Integer maxConcurrency;
private TreeMap<Long, List<ITxEvent>> events;
private List<RelationType> usedRelationsList;
public TxStream(IDatabase database, IWaveformDb waveformDb, ScvStream scvStream) {
super(scvStream.getName());
this.database=database;
fullName=scvStream.getName();
this.scvStream=scvStream;
db=waveformDb;
}
@Override
public IWaveformDb getDb() {
return db;
}
@Override
public String getFullName() {
return fullName;
}
@Override
public Long getId() {
return (long) scvStream.getId();
}
@Override
public String getKind() {
return scvStream.getKind();
}
@Override
public List<ITxGenerator> getGenerators() {
if(generators==null){
SQLiteDatabaseSelectHandler<ScvGenerator> handler = new SQLiteDatabaseSelectHandler<ScvGenerator>(
ScvGenerator.class, database, "stream="+scvStream.getId());
generators=new TreeMap<Integer, TxGenerator>();
try {
for(ScvGenerator scvGenerator:handler.selectObjects()){
generators.put(scvGenerator.getId(), new TxGenerator(this, scvGenerator));
}
} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
| InvocationTargetException | SQLException | IntrospectionException e) {
e.printStackTrace();
}
}
return new ArrayList<ITxGenerator>(generators.values());
}
@Override
public int getMaxConcurrency() {
if(maxConcurrency==null){
java.sql.Connection connection=null;
java.sql.Statement statement=null;
java.sql.ResultSet resultSet=null;
try {
connection = database.createConnection();
statement = connection.createStatement();
StringBuilder sb = new StringBuilder();
sb.append("SELECT MAX(concurrencyLevel) as concurrencyLevel FROM ScvTx where stream=");
sb.append(scvStream.getId());
resultSet = statement.executeQuery(sb.toString());
while (resultSet.next()) {
Object value = resultSet.getObject("concurrencyLevel");
if(value!=null)
maxConcurrency=(Integer) value;
}
} catch (SQLException e) {
if(maxConcurrency==null) maxConcurrency=0;
} finally {
try{
if(resultSet!=null) resultSet.close();
if(statement!=null) statement.close();
if(connection!=null) connection.close();
} catch (SQLException e) { }
}
maxConcurrency+=1;
}
return maxConcurrency;
}
@Override
public NavigableMap<Long, List<ITxEvent>> getEvents(){
if(events==null){
events=new TreeMap<Long, List<ITxEvent>>();
for(Entry<Integer, ITx> entry:getTransactions().entrySet()){
putEvent(new TxEvent(TxEvent.Type.BEGIN, entry.getValue()));
putEvent(new TxEvent(TxEvent.Type.END, entry.getValue()));
}
}
return events;
}
private void putEvent(TxEvent ev){
Long time = ev.getTime();
if(!events.containsKey(time)){
Vector<ITxEvent> vector=new Vector<ITxEvent>();
vector.add(ev);
events.put(time, vector);
} else {
events.get(time).add(ev);
}
}
protected Map<Integer, ITx> getTransactions() {
if(transactions==null){
if(generators==null) getGenerators();
transactions = new TreeMap<Integer, ITx>();
SQLiteDatabaseSelectHandler<ScvTx> handler = new SQLiteDatabaseSelectHandler<ScvTx>(ScvTx.class, database,
"stream="+scvStream.getId());
try {
for(ScvTx scvTx:handler.selectObjects()){
transactions.put(scvTx.getId(), new Tx(database, this, generators.get(scvTx.getGenerator()), scvTx));
}
} catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException
| InvocationTargetException | SQLException | IntrospectionException e) {
e.printStackTrace();
}
}
return transactions;
}
@Override
public Collection<ITxEvent> getWaveformEventsAtTime(Long time) {
return getEvents().get(time);
}
public void setRelationTypeList(List<RelationType> usedRelationsList){
this.usedRelationsList=usedRelationsList;
}
public RelationType getRelationType(String name) {
RelationType relType=RelationType.create(name);
if(!usedRelationsList.contains(relType)) usedRelationsList.add(relType);
return relType;
}
@Override
public Boolean equals(IWaveform<? extends IWaveformEvent> other) {
return(other instanceof TxStream && this.getId()==other.getId());
}
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,3 +0,0 @@
eclipse.preferences.version=1
encoding//src/com/minres/scviewer/database/test/DatabaseServicesTest.java=UTF-8
encoding/<project>=UTF-8

View File

@ -1,8 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
<booleanAttribute key="append.args" value="true"/>
<stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
<booleanAttribute key="askclear" value="false"/>
<booleanAttribute key="automaticAdd" value="true"/>
<booleanAttribute key="automaticValidate" value="false"/>
<stringAttribute key="bootstrap" value=""/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="clearConfig" value="true"/>
<booleanAttribute key="clearws" value="true"/>
<booleanAttribute key="clearwslog" value="false"/>
<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
<booleanAttribute key="default" value="false"/>
<stringAttribute key="deselected_workspace_plugins" value="com.minres.scviewer.e4.application,com.minres.scviewer.ui"/>
<booleanAttribute key="includeOptional" value="true"/>
<stringAttribute key="location" value="${workspace_loc}/../junit-workspace"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.minres.scviewer.database.test"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.minres.scviewer.database.test"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.minres.scviewer.database.test"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx512m"/>
<stringAttribute key="pde.version" value="3.3"/>
<stringAttribute key="product" value="com.minres.scviewer.e4.product"/>
<booleanAttribute key="run_in_ui_thread" value="true"/>
<stringAttribute key="selected_target_plugins" value="com.google.guava@default:default,javax.annotation@default:default,javax.inject@default:default,javax.servlet@default:default,javax.xml@default:default,org.apache.ant@default:default,org.apache.commons.jxpath@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.codehaus.groovy@default:default,org.eclipse.ant.core@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.e4.core.contexts@default:default,org.eclipse.e4.core.di.annotations@default:default,org.eclipse.e4.core.di.extensions@default:default,org.eclipse.e4.core.di@default:default,org.eclipse.e4.core.services@default:default,org.eclipse.e4.emf.xpath@default:default,org.eclipse.e4.ui.di@default:default,org.eclipse.e4.ui.model.workbench@default:default,org.eclipse.e4.ui.services@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.bidi@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.ds@1:true,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.util@default:default,org.eclipse.jface@default:default,org.eclipse.osgi.compatibility.state@default:false,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.swt@default:default,org.hamcrest.core@default:default,org.junit@default:default"/>
<stringAttribute key="selected_workspace_plugins" value="com.minres.scviewer.database.binary@default:default,com.minres.scviewer.database.sqlite@default:true,com.minres.scviewer.database.test@default:default,com.minres.scviewer.database.text@default:true,com.minres.scviewer.database.ui.swt@default:default,com.minres.scviewer.database.ui@default:default,com.minres.scviewer.database.vcd@default:default,com.minres.scviewer.database@default:true,com.opcoach.e4.preferences@default:default"/>
<booleanAttribute key="show_selected_only" value="false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfig" value="true"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<booleanAttribute key="useProduct" value="false"/>
</launchConfiguration>

View File

@ -1,2 +0,0 @@
/.scviewer.*
/.my_db.txlog*

View File

@ -1,90 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.minres.scviewer.database.IWaveformDb;
import com.minres.scviewer.database.IWaveformDbFactory;
public class DatabaseServicesTest {
private static IWaveformDbFactory waveformDbFactory;
private IWaveformDb waveformDb;
public synchronized void setFactory(IWaveformDbFactory service) {
waveformDbFactory = service;
}
public synchronized void unsetFactory(IWaveformDbFactory service) {
if (waveformDbFactory == service) {
waveformDbFactory = null;
}
}
@Before
public void setUp() throws Exception {
waveformDb=waveformDbFactory.getDatabase();
// Wait for OSGi dependencies
// for (int i = 0; i < 10; i++) {
// if (waveformDb.size() == 3) // Dependencies fulfilled
// return;
// Thread.sleep(1000);
// }
// assertEquals("OSGi dependencies unfulfilled", 3, WaveformDb.getLoaders().size());
}
@After
public void tearDown() throws Exception {
}
@Test
public void testVCD() throws Exception {
File f = new File("inputs/my_db.vcd").getAbsoluteFile();
assertTrue(f.exists());
waveformDb.load(f);
assertNotNull(waveformDb);
assertEquals(14, waveformDb.getAllWaves().size());
assertEquals(2, waveformDb.getChildNodes().size());
}
@Test
public void testTxSQLite() throws Exception {
File f = new File("inputs/my_db.txdb").getAbsoluteFile();
assertTrue(f.exists());
waveformDb.load(f);
assertNotNull(waveformDb);
assertEquals(3, waveformDb.getAllWaves().size());
assertEquals(1, waveformDb.getChildNodes().size());
}
@Test
public void testTxText() throws Exception {
File f = new File("inputs/my_db.txlog").getAbsoluteFile();
assertTrue(f.exists());
waveformDb.load(f);
assertNotNull(waveformDb);
assertEquals(3, waveformDb.getAllWaves().size());
assertEquals(1, waveformDb.getChildNodes().size());
}
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,8 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,17 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Textual transaction database
Bundle-SymbolicName: com.minres.scviewer.database.text
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.minres.scviewer.database,
org.osgi.framework;version="1.3.0"
Require-Bundle: com.minres.scviewer.database;bundle-version="1.0.0",
org.codehaus.groovy;bundle-version="1.8.6",
org.eclipse.equinox.util;bundle-version="1.0.500",
org.eclipse.equinox.ds;bundle-version="1.4.200",
org.eclipse.osgi.services;bundle-version="3.4.0",
com.google.guava;bundle-version="15.0.0"
Service-Component: OSGI-INF/component.xml
Bundle-ActivationPolicy: lazy

View File

@ -1,42 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>com.minres.scviewer.database.text</artifactId>
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../com.minres.scviewer.parent</relativePath>
</parent>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-plugin</artifactId>
<version>0.23.1</version>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
<!-- set verbose to be true if you want lots of uninteresting messages -->
<!-- <verbose>true</verbose> -->
<source>1.7</source>
<target>1.7</target>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>${groovy-eclipse-compiler-version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>${groovy-eclipse-batch-version}</version>
<!-- or choose a different compiler version -->
<!-- <version>2.1.8-01</version> -->
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,221 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text;
import java.nio.charset.CharsetDecoder;
import java.util.Collection;
import java.util.zip.GZIPInputStream
import org.apache.jdbm.DB
import org.apache.jdbm.DBMaker
import groovy.io.FileType
import com.minres.scviewer.database.AssociationType
import com.minres.scviewer.database.DataType
import com.minres.scviewer.database.ITxGenerator
import com.minres.scviewer.database.ITxStream
import com.minres.scviewer.database.IWaveform
import com.minres.scviewer.database.IWaveformDb
import com.minres.scviewer.database.IWaveformDbLoader
import com.minres.scviewer.database.RelationType
public class TextDbLoader implements IWaveformDbLoader{
private Long maxTime;
IWaveformDb db;
DB backingDb;
def streams = []
def relationTypes=[:]
public TextDbLoader() {
}
@Override
public Long getMaxTime() {
return maxTime;
}
@Override
public List<IWaveform> getAllWaves() {
return new LinkedList<IWaveform>(streams);
}
public Map<Long, ITxGenerator> getGeneratorsById() {
TreeMap<Long, ITxGenerator> res = new TreeMap<Long, ITxGenerator>();
streams.each{TxStream stream -> stream.generators.each{res.put(it.id, id)} }
return res;
}
static final byte[] x = "scv_tr_stream".bytes
@Override
boolean load(IWaveformDb db, File file) throws Exception {
this.db=db
this.streams=[]
def gzipped = isGzipped(file)
if(isTxfile(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file))){
if(true) {
def parentDir=file.absoluteFile.parent
def filename=file.name
new File(parentDir).eachFileRecurse (FileType.FILES) { f -> if(f.name=~/^\.${filename}/) f.delete() }
this.backingDb = DBMaker.openFile(parentDir+File.separator+"."+filename+"_bdb")
.deleteFilesAfterClose()
.useRandomAccessFile()
.setMRUCacheSize(4096)
//.disableTransactions()
.disableLocking()
.make();
} else {
this.backingDb = DBMaker.openMemory().disableLocking().make()
}
parseInput(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file))
calculateConcurrencyIndicees()
return true
}
return false;
}
private static boolean isTxfile(InputStream istream) {
byte[] buffer = new byte[x.size()]
def readCnt = istream.read(buffer, 0, x.size())
istream.close()
if(readCnt==x.size()){
for(int i=0; i<x.size(); i++)
if(buffer[i]!=x[i]) return false
}
return true
}
private static boolean isGzipped(File f) {
InputStream is = null;
try {
is = new FileInputStream(f);
byte [] signature = new byte[2];
int nread = is.read( signature ); //read the gzip signature
return nread == 2 && signature[ 0 ] == (byte) 0x1f && signature[ 1 ] == (byte) 0x8b;
} catch (IOException e) {
return false;
} finally {
is.close()
}
}
private stringToScale(String scale){
switch(scale.trim()){
case "fs":return 1L
case "ps":return 1000L
case "ns":return 1000000L
case "us":return 1000000000L
case "ms":return 1000000000000L
case "s": return 1000000000000000L
}
}
private def parseInput(InputStream inputStream){
def streamsById = [:]
def generatorsById = [:]
def transactionsById = [:]
TxGenerator generator
Tx transaction
boolean endTransaction=false
def matcher
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
long lineCnt=0;
reader.eachLine { line ->
def tokens = line.split(/\s+/)
switch(tokens[0]){
case "scv_tr_stream":
case "scv_tr_generator":
case "begin_attribute":
case "end_attribute":
if ((matcher = line =~ /^scv_tr_stream\s+\(ID (\d+),\s+name\s+"([^"]+)",\s+kind\s+"([^"]+)"\)$/)) {
def id = Integer.parseInt(matcher[0][1])
def stream = new TxStream(db, id, matcher[0][2], matcher[0][3], backingDb)
streams<<stream
streamsById[id]=stream
} else if ((matcher = line =~ /^scv_tr_generator\s+\(ID\s+(\d+),\s+name\s+"([^"]+)",\s+scv_tr_stream\s+(\d+),$/)) {
def id = Integer.parseInt(matcher[0][1])
ITxStream stream=streamsById[Integer.parseInt(matcher[0][3])]
generator=new TxGenerator(id, stream, matcher[0][2])
stream.generators<<generator
generatorsById[id]=generator
} else if ((matcher = line =~ /^begin_attribute \(ID (\d+), name "([^"]+)", type "([^"]+)"\)$/)) {
generator.begin_attrs << TxAttributeType.getAttrType(matcher[0][2], DataType.valueOf(matcher[0][3]), AssociationType.BEGIN)
} else if ((matcher = line =~ /^end_attribute \(ID (\d+), name "([^"]+)", type "([^"]+)"\)$/)) {
generator.end_attrs << TxAttributeType.getAttrType(matcher[0][2], DataType.valueOf(matcher[0][3]), AssociationType.END)
}
break;
case ")":
generator=null
break
case "tx_begin"://matcher = line =~ /^tx_begin\s+(\d+)\s+(\d+)\s+(\d+)\s+([munpf]?s)/
def id = Integer.parseInt(tokens[1])
TxGenerator gen=generatorsById[Integer.parseInt(tokens[2])]
transaction = new Tx(id, gen.stream, gen, Long.parseLong(tokens[3])*stringToScale(tokens[4]))
gen.transactions << transaction
transactionsById[id]= transaction
gen.begin_attrs_idx=0;
maxTime = maxTime>transaction.beginTime?maxTime:transaction.beginTime
endTransaction=false
break
case "tx_end"://matcher = line =~ /^tx_end\s+(\d+)\s+(\d+)\s+(\d+)\s+([munpf]?s)/
def id = Integer.parseInt(tokens[1])
transaction = transactionsById[id]
assert Integer.parseInt(tokens[2])==transaction.generator.id
transaction.endTime = Long.parseLong(tokens[3])*stringToScale(tokens[4])
transaction.generator.end_attrs_idx=0;
maxTime = maxTime>transaction.endTime?maxTime:transaction.endTime
endTransaction=true
break
case "tx_record_attribute"://matcher = line =~ /^tx_record_attribute\s+(\d+)\s+"([^"]+)"\s+(\S+)\s*=\s*(.+)$/
def id = Integer.parseInt(tokens[1])
transactionsById[id].attributes<<new TxAttribute(tokens[2][1..-2], DataType.valueOf(tokens[3]), AssociationType.RECORD, tokens[5..-1].join(' '))
break
case "a"://matcher = line =~ /^a\s+(.+)$/
if(endTransaction){
transaction.attributes << new TxAttribute(transaction.generator.end_attrs[0], tokens[1])
} else {
transaction.attributes << new TxAttribute(transaction.generator.begin_attrs[0], tokens[1])
}
break
case "tx_relation"://matcher = line =~ /^tx_relation\s+\"(\S+)\"\s+(\d+)\s+(\d+)$/
Tx tr2= transactionsById[Integer.parseInt(tokens[2])]
Tx tr1= transactionsById[Integer.parseInt(tokens[3])]
def relType=tokens[1][1..-2]
if(!relationTypes.containsKey(relType)) relationTypes[relType]=RelationType.create(relType)
def rel = new TxRelation(relationTypes[relType], tr1, tr2)
tr1.outgoingRelations<<rel
tr2.incomingRelations<<rel
break
default:
println "Don't know what to do with: '$line'"
}
lineCnt++
if((lineCnt%1000) == 0) {
backingDb.commit()
}
}
}
private def calculateConcurrencyIndicees(){
streams.each{ TxStream stream -> stream.getMaxConcurrency() }
}
public Collection<RelationType> getAllRelationTypes(){
return relationTypes.values();
}
}

View File

@ -1,67 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import com.minres.scviewer.database.*
class Tx implements ITx {
Long id
TxGenerator generator
TxStream stream
int concurrencyIndex
Long beginTime
Long endTime
ArrayList<ITxAttribute> attributes = new ArrayList<ITxAttribute>()
def incomingRelations =[]
def outgoingRelations =[]
Tx(int id, TxStream stream, TxGenerator generator, Long begin){
this.id=id
this.stream=stream
this.generator=generator
this.beginTime=begin
this.endTime=begin
}
@Override
public Collection<ITxRelation> getIncomingRelations() {
return incomingRelations;
}
@Override
public Collection<ITxRelation> getOutgoingRelations() {
return outgoingRelations;
}
@Override
public int compareTo(ITx o) {
def res =beginTime.compareTo(o.beginTime)
if(res!=0)
return res
else
return id.compareTo(o.id)
}
@Override
public String toString() {
return "tx#"+getId()+"["+getBeginTime()/1000000+"ns - "+getEndTime()/1000000+"ns]";
}
}

View File

@ -1,59 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import com.minres.scviewer.database.AssociationType;
import com.minres.scviewer.database.DataType;
import com.minres.scviewer.database.ITxAttributeType;
import com.minres.scviewer.database.ITxAttribute
class TxAttribute implements ITxAttribute{
TxAttributeType attributeType
def value
TxAttribute(String name, DataType dataType, AssociationType type, value){
attributeType = TxAttributeTypeFactory.instance.getAttrType(name, dataType, type)
switch(dataType){
case DataType.STRING:
case DataType.ENUMERATION:
this.value=value[1..-2]
break;
default:
this.value=value
}
}
TxAttribute(TxAttributeType other){
attributeType=other
}
TxAttribute(TxAttributeType other, value){
this(other.name, other.dataType, other.type, value)
}
@Override
public String getName() {
return attributeType.getName();
}
@Override
public AssociationType getType() {
attributeType.type;
}
@Override
public DataType getDataType() {
attributeType.dataType;
}
}

View File

@ -1,31 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import com.minres.scviewer.database.AssociationType;
import com.minres.scviewer.database.DataType;
import com.minres.scviewer.database.ITxAttributeType;
class TxAttributeType implements ITxAttributeType {
String name
DataType dataType
AssociationType type
static TxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
TxAttributeTypeFactory.instance.getAttrType(name, dataType, type)
}
TxAttributeType(String name, DataType dataType, AssociationType type){
this.name=name
this.dataType=dataType
this.type=type
}
}

View File

@ -1,38 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import com.minres.scviewer.database.AssociationType;
import com.minres.scviewer.database.DataType
import com.minres.scviewer.database.ITxAttributeType
import com.minres.scviewer.database.ITxAttribute
class TxAttributeTypeFactory {
private static final instance = new TxAttributeTypeFactory()
def attributes = [:]
private TxAttributeTypeFactory() {
TxAttributeTypeFactory.metaClass.constructor = {-> instance }
}
ITxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
def key = name+":"+dataType.toString()
ITxAttributeType res
if(attributes.containsKey(key)){
res=attributes[key]
} else {
res=new TxAttributeType(name, dataType, type)
attributes[key]=res
}
return res
}
}

View File

@ -1,36 +0,0 @@
package com.minres.scviewer.database.text;
import com.minres.scviewer.database.ITx
import com.minres.scviewer.database.ITxEvent
import com.minres.scviewer.database.IWaveformEvent
class TxEvent implements ITxEvent {
final ITxEvent.Type type;
final Tx transaction;
final Long time
TxEvent(ITxEvent.Type type, ITx transaction) {
super();
this.type = type;
this.transaction = transaction;
this.time = type==ITxEvent.Type.BEGIN?transaction.beginTime:transaction.endTime
}
@Override
IWaveformEvent duplicate() throws CloneNotSupportedException {
new TxEvent(type, transaction, time)
}
@Override
int compareTo(IWaveformEvent o) {
time.compareTo(o.time)
}
@Override
String toString() {
type.toString()+"@"+time+" of tx #"+transaction.id;
}
}

View File

@ -1,52 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import java.util.ArrayList;
import java.util.List;
import com.minres.scviewer.database.ITxAttributeType
import com.minres.scviewer.database.ITxAttribute;
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.ITxGenerator;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.IWaveformEvent;
class TxGenerator implements ITxGenerator{
Long id
TxStream stream
String name
Boolean active = false
ArrayList<ITx> transactions=[]
ArrayList<ITxAttributeType> begin_attrs = []
int begin_attrs_idx = 0
ArrayList<ITxAttributeType> end_attrs= []
int end_attrs_idx = 0
TxGenerator(int id, TxStream stream, name){
this.id=id
this.stream=stream
this.name=name
}
ITxStream<? extends ITxEvent> getStream(){
return stream;
}
List<ITx> getTransactions(){
return transactions
}
Boolean isActive() {return active};
}

View File

@ -1,36 +0,0 @@
package com.minres.scviewer.database.text
import com.minres.scviewer.database.ITxRelation
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.RelationType;
class TxRelation implements ITxRelation {
Tx source
Tx target
RelationType relationType
public TxRelation(RelationType relationType, Tx source, Tx target) {
this.source = source;
this.target = target;
this.relationType = relationType;
}
@Override
public RelationType getRelationType() {
return relationType;
}
@Override
public ITx getSource() {
return source;
}
@Override
public ITx getTarget() {
return target;
}
}

View File

@ -1,117 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import org.apache.jdbm.DB
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformDb
import com.minres.scviewer.database.IWaveformEvent
import com.minres.scviewer.database.ITxGenerator
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.IHierNode
import com.minres.scviewer.database.ITxStream
import com.minres.scviewer.database.ITx
class TxStream extends HierNode implements ITxStream {
Long id
IWaveformDb database
String fullName
String kind
def generators = []
int maxConcurrency
private TreeMap<Long, List<ITxEvent>> events
TxStream(IWaveformDb db, int id, String name, String kind, DB backingStore){
super(name)
this.id=id
this.database=db
this.fullName=name
this.kind=kind
this.maxConcurrency=0
//events = new TreeMap<Long, List<ITxEvent>>()
events=backingStore.createTreeMap("stream-"+name)
}
List<ITxGenerator> getGenerators(){
return generators as List<ITxGenerator>
}
@Override
public IWaveformDb getDb() {
return database
}
@Override
public int getMaxConcurrency() {
if(!maxConcurrency){
generators.each {TxGenerator generator ->
generator.transactions.each{ Tx tx ->
putEvent(new TxEvent(ITxEvent.Type.BEGIN, tx))
putEvent(new TxEvent(ITxEvent.Type.END, tx))
}
}
def rowendtime = [0]
events.keySet().each{long time ->
def value=events.get(time)
def starts=value.findAll{ITxEvent event ->event.type==ITxEvent.Type.BEGIN}
starts.each {ITxEvent event ->
Tx tx = event.transaction
def rowIdx = 0
for(rowIdx=0; rowIdx<rowendtime.size() && rowendtime[rowIdx]>tx.beginTime; rowIdx++);
if(rowendtime.size<=rowIdx)
rowendtime<<tx.endTime
else
rowendtime[rowIdx]=tx.endTime
tx.concurrencyIndex=rowIdx
}
}
maxConcurrency=rowendtime.size()
}
return maxConcurrency
}
private putEvent(ITxEvent event){
if(!events.containsKey(event.time))
events.put(event.time, [event])
else
events[event.time]<<event
}
@Override
public NavigableMap getEvents() {
return events;
}
@Override
public Collection getWaveformEventsAtTime(Long time) {
return events.get(time);
}
@Override
public Boolean equals(IWaveform<? extends IWaveformEvent> other) {
return(other instanceof TxStream && this.getId()==other.getId());
}
}

View File

@ -1,706 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* B+Tree persistent indexing data structure. B+Trees are optimized for
* block-based, random I/O storage because they store multiple keys on
* one tree node (called <code>BTreeNode</code>). In addition, the leaf nodes
* directly contain (inline) small values associated with the keys, allowing a
* single (or sequential) disk read of all the values on the node.
* <p/>
* B+Trees are n-airy, yeilding log(N) search cost. They are self-balancing,
* preventing search performance degradation when the size of the tree grows.
* <p/>
* BTree stores its keys sorted. By default JDBM expects key to implement
* <code>Comparable</code> interface but user may supply its own <code>Comparator</code>
* at BTree creation time. Comparator is serialized and stored as part of BTree.
* <p/>
* The B+Tree allows traversing the keys in forward and reverse order using a
* TupleBrowser obtained from the browse() methods. But it is better to use
* <code>BTreeMap</code> wrapper which implements <code>SortedMap</code> interface
* <p/>
* This implementation does not directly support duplicate keys. It is
* possible to handle duplicates by grouping values using an ArrayList as value.
* This scenario is supported by JDBM serialization so there is no big performance penalty.
* <p/>
* There is no limit on key size or value size, but it is recommended to keep
* keys as small as possible to reduce disk I/O. If serialized value exceeds 32 bytes,
* it is stored in separate record and tree contains only recid reference to it.
* BTree uses delta compression for its keys.
*
*
* @author Alex Boisvert
* @author Jan Kotek
*/
class BTree<K, V> {
private static final boolean DEBUG = false;
/**
* Default node size (number of entries per node)
*/
public static final int DEFAULT_SIZE = 32; //TODO test optimal size, it has serious impact on sequencial write and read
/**
* Record manager used to persist changes in BTreeNodes
*/
protected transient DBAbstract _db;
/**
* This BTree's record ID in the DB.
*/
private transient long _recid;
/**
* Comparator used to index entries (optional)
*/
protected Comparator<K> _comparator;
/**
* Serializer used to serialize index keys (optional)
*/
protected Serializer<K> keySerializer;
/**
* Serializer used to serialize index values (optional)
*/
protected Serializer<V> valueSerializer;
/**
* indicates if values should be loaded during deserialization, set to false during defragmentation
*/
boolean loadValues = true;
/** if false map contains only keys, used for set*/
boolean hasValues = true;
/**
* The number of structural modifications to the tree for fail fast iterators. This value is just for runtime, it is not persisted
*/
transient int modCount = 0;
/**
* cached instance of an insert result, so we do not have to allocate new object on each insert
*/
protected BTreeNode.InsertResult<K, V> insertResultReuse; //TODO investigate performance impact of removing this
public Serializer<K> getKeySerializer() {
return keySerializer;
}
public Serializer<V> getValueSerializer() {
return valueSerializer;
}
/**
* Height of the B+Tree. This is the number of BTreeNodes you have to traverse
* to get to a leaf BTreeNode, starting from the root.
*/
private int _height;
/**
* Recid of the root BTreeNode
*/
private transient long _root;
/**
* Total number of entries in the BTree
*/
protected volatile long _entries;
/**
* Serializer used for BTreeNodes of this tree
*/
private transient BTreeNode<K, V> _nodeSerializer = new BTreeNode();
{
_nodeSerializer._btree = this;
}
/**
* Listeners which are notified about changes in records
*/
protected RecordListener[] recordListeners = new RecordListener[0];
final protected ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* No-argument constructor used by serialization.
*/
public BTree() {
// empty
}
/**
* Create a new persistent BTree
*/
@SuppressWarnings("unchecked")
public static <K extends Comparable, V> BTree<K, V> createInstance(DBAbstract db)
throws IOException {
return createInstance(db, null, null, null,true);
}
/**
* Create a new persistent BTree
*/
public static <K, V> BTree<K, V> createInstance(DBAbstract db,
Comparator<K> comparator,
Serializer<K> keySerializer,
Serializer<V> valueSerializer,
boolean hasValues)
throws IOException {
BTree<K, V> btree;
if (db == null) {
throw new IllegalArgumentException("Argument 'db' is null");
}
btree = new BTree<K, V>();
btree._db = db;
btree._comparator = comparator;
btree.keySerializer = keySerializer;
btree.valueSerializer = valueSerializer;
btree.hasValues = hasValues;
btree._recid = db.insert(btree, btree.getRecordManager().defaultSerializer(),false);
return btree;
}
/**
* Load a persistent BTree.
*
* @param db DB used to store the persistent btree
* @param recid Record id of the BTree
*/
@SuppressWarnings("unchecked")
public static <K, V> BTree<K, V> load(DBAbstract db, long recid)
throws IOException {
BTree<K, V> btree = (BTree<K, V>) db.fetch(recid);
btree._recid = recid;
btree._db = db;
btree._nodeSerializer = new BTreeNode<K, V>();
btree._nodeSerializer._btree = btree;
return btree;
}
/**
* Get the {@link ReadWriteLock} associated with this BTree.
* This should be used with browsing operations to ensure
* consistency.
*
* @return
*/
public ReadWriteLock getLock() {
return lock;
}
/**
* Insert an entry in the BTree.
* <p/>
* The BTree cannot store duplicate entries. An existing entry can be
* replaced using the <code>replace</code> flag. If an entry with the
* same key already exists in the BTree, its value is returned.
*
* @param key Insert key
* @param value Insert value
* @param replace Set to true to replace an existing key-value pair.
* @return Existing value, if any.
*/
public V insert(final K key, final V value,
final boolean replace)
throws IOException {
if (key == null) {
throw new IllegalArgumentException("Argument 'key' is null");
}
if (value == null) {
throw new IllegalArgumentException("Argument 'value' is null");
}
try {
lock.writeLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode == null) {
// BTree is currently empty, create a new root BTreeNode
if (DEBUG) {
System.out.println("BTree.insert() new root BTreeNode");
}
rootNode = new BTreeNode<K, V>(this, key, value);
_root = rootNode._recid;
_height = 1;
_entries = 1;
_db.update(_recid, this);
modCount++;
//notifi listeners
for (RecordListener<K, V> l : recordListeners) {
l.recordInserted(key, value);
}
return null;
} else {
BTreeNode.InsertResult<K, V> insert = rootNode.insert(_height, key, value, replace);
boolean dirty = false;
if (insert._overflow != null) {
// current root node overflowed, we replace with a new root node
if (DEBUG) {
System.out.println("BTreeNode.insert() replace root BTreeNode due to overflow");
}
rootNode = new BTreeNode<K, V>(this, rootNode, insert._overflow);
_root = rootNode._recid;
_height += 1;
dirty = true;
}
if (insert._existing == null) {
_entries++;
modCount++;
dirty = true;
}
if (dirty) {
_db.update(_recid, this);
}
//notify listeners
for (RecordListener<K, V> l : recordListeners) {
if (insert._existing == null)
l.recordInserted(key, value);
else
l.recordUpdated(key, insert._existing, value);
}
// insert might have returned an existing value
V ret = insert._existing;
//zero out tuple and put it for reuse
insert._existing = null;
insert._overflow = null;
this.insertResultReuse = insert;
return ret;
}
} finally {
lock.writeLock().unlock();
}
}
/**
* Remove an entry with the given key from the BTree.
*
* @param key Removal key
* @return Value associated with the key, or null if no entry with given
* key existed in the BTree.
*/
public V remove(K key)
throws IOException {
if (key == null) {
throw new IllegalArgumentException("Argument 'key' is null");
}
try {
lock.writeLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode == null) {
return null;
}
boolean dirty = false;
BTreeNode.RemoveResult<K, V> remove = rootNode.remove(_height, key);
if (remove._underflow && rootNode.isEmpty()) {
_height -= 1;
dirty = true;
_db.delete(_root);
if (_height == 0) {
_root = 0;
} else {
_root = rootNode.loadLastChildNode()._recid;
}
}
if (remove._value != null) {
_entries--;
modCount++;
dirty = true;
}
if (dirty) {
_db.update(_recid, this);
}
if (remove._value != null)
for (RecordListener<K, V> l : recordListeners)
l.recordRemoved(key, remove._value);
return remove._value;
} finally {
lock.writeLock().unlock();
}
}
/**
* Find the value associated with the given key.
*
* @param key Lookup key.
* @return Value associated with the key, or null if not found.
*/
public V get(K key)
throws IOException {
if (key == null) {
throw new IllegalArgumentException("Argument 'key' is null");
}
try {
lock.readLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode == null) {
return null;
}
return rootNode.findValue(_height, key);
} finally {
lock.readLock().unlock();
}
}
/**
* Find the value associated with the given key, or the entry immediately
* following this key in the ordered BTree.
*
* @param key Lookup key.
* @return Value associated with the key, or a greater entry, or null if no
* greater entry was found.
*/
public BTreeTuple<K, V> findGreaterOrEqual(K key)
throws IOException {
BTreeTuple<K, V> tuple;
BTreeTupleBrowser<K, V> browser;
if (key == null) {
// there can't be a key greater than or equal to "null"
// because null is considered an infinite key.
return null;
}
tuple = new BTreeTuple<K, V>(null, null);
browser = browse(key,true);
if (browser.getNext(tuple)) {
return tuple;
} else {
return null;
}
}
/**
* Get a browser initially positioned at the beginning of the BTree.
* <p><b>
* WARNING: If you make structural modifications to the BTree during
* browsing, you will get inconsistent browing results.
* </b>
*
* @return Browser positionned at the beginning of the BTree.
*/
@SuppressWarnings("unchecked")
public BTreeTupleBrowser<K, V> browse()
throws IOException {
try {
lock.readLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode == null) {
return EMPTY_BROWSER;
}
return rootNode.findFirst();
} finally {
lock.readLock().unlock();
}
}
/**
* Get a browser initially positioned just before the given key.
* <p><b>
* WARNING: <EFBFBD>If you make structural modifications to the BTree during
* browsing, you will get inconsistent browing results.
* </b>
*
* @param key Key used to position the browser. If null, the browser
* will be positionned after the last entry of the BTree.
* (Null is considered to be an "infinite" key)
* @return Browser positionned just before the given key.
*/
@SuppressWarnings("unchecked")
public BTreeTupleBrowser<K, V> browse(final K key, final boolean inclusive)
throws IOException {
try {
lock.readLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode == null) {
return EMPTY_BROWSER;
}
BTreeTupleBrowser<K, V> browser = rootNode.find(_height, key, inclusive);
return browser;
} finally {
lock.readLock().unlock();
}
}
/**
* Return the persistent record identifier of the BTree.
*/
public long getRecid() {
return _recid;
}
/**
* Return the root BTreeNode, or null if it doesn't exist.
*/
BTreeNode<K, V> getRoot()
throws IOException {
if (_root == 0) {
return null;
}
BTreeNode<K, V> root = _db.fetch(_root, _nodeSerializer);
if (root != null) {
root._recid = _root;
root._btree = this;
}
return root;
}
static BTree readExternal(DataInput in, Serialization ser)
throws IOException, ClassNotFoundException {
BTree tree = new BTree();
tree._db = ser.db;
tree._height = in.readInt();
tree._recid = in.readLong();
tree._root = in.readLong();
tree._entries = in.readLong();
tree.hasValues = in.readBoolean();
tree._comparator = (Comparator) ser.deserialize(in);
tree.keySerializer = (Serializer) ser.deserialize(in);
tree.valueSerializer = (Serializer) ser.deserialize(in);
return tree;
}
public void writeExternal(DataOutput out)
throws IOException {
out.writeInt(_height);
out.writeLong(_recid);
out.writeLong(_root);
out.writeLong(_entries);
out.writeBoolean(hasValues);
_db.defaultSerializer().serialize(out, _comparator);
_db.defaultSerializer().serialize(out, keySerializer);
_db.defaultSerializer().serialize(out, valueSerializer);
}
/**
* Copyes tree from one db to other, defragmenting it allong the way
* @param recid
* @param r1
* @param r2
* @throws IOException
*/
public static void defrag(long recid, DBStore r1, DBStore r2) throws IOException {
try {
byte[] data = r1.fetchRaw(recid);
r2.forceInsert(recid, data);
DataInput in = new DataInputOutput(data);
BTree t = (BTree) r1.defaultSerializer().deserialize(in);
t.loadValues = false;
t._db = r1;
t._nodeSerializer = new BTreeNode(t, false);
BTreeNode p = t.getRoot();
if (p != null) {
r2.forceInsert(t._root, r1.fetchRaw(t._root));
p.defrag(r1, r2);
}
} catch (ClassNotFoundException e) {
throw new IOError(e);
}
}
/**
* Browser returning no element.
*/
private static final BTreeTupleBrowser EMPTY_BROWSER = new BTreeTupleBrowser() {
public boolean getNext(BTreeTuple tuple) {
return false;
}
public boolean getPrevious(BTreeTuple tuple) {
return false;
}
public void remove(Object key) {
throw new IndexOutOfBoundsException();
}
};
/**
* add RecordListener which is notified about record changes
*
* @param listener
*/
public void addRecordListener(RecordListener<K, V> listener) {
recordListeners = Arrays.copyOf(recordListeners, recordListeners.length + 1);
recordListeners[recordListeners.length - 1] = listener;
}
/**
* remove RecordListener which is notified about record changes
*
* @param listener
*/
public void removeRecordListener(RecordListener<K, V> listener) {
List l = Arrays.asList(recordListeners);
l.remove(listener);
recordListeners = (RecordListener[]) l.toArray(new RecordListener[1]);
}
public DBAbstract getRecordManager() {
return _db;
}
public Comparator<K> getComparator() {
return _comparator;
}
/**
* Deletes all BTreeNodes in this BTree
*/
public void clear()
throws IOException {
try {
lock.writeLock().lock();
BTreeNode<K, V> rootNode = getRoot();
if (rootNode != null)
rootNode.delete();
_entries = 0;
modCount++;
} finally {
lock.writeLock().unlock();
}
}
/**
* Used for debugging and testing only. Populates the 'out' list with
* the recids of all child nodes in the BTree.
*
* @param out
* @throws IOException
*/
void dumpChildNodeRecIDs(List<Long> out) throws IOException {
BTreeNode<K, V> root = getRoot();
if (root != null) {
out.add(root._recid);
root.dumpChildNodeRecIDs(out, _height);
}
}
public boolean hasValues() {
return hasValues;
}
/**
* Browser to traverse a collection of tuples. The browser allows for
* forward and reverse order traversal.
*
*
*/
static interface BTreeTupleBrowser<K, V> {
/**
* Get the next tuple.
*
* @param tuple Tuple into which values are copied.
* @return True if values have been copied in tuple, or false if there is no next tuple.
*/
boolean getNext(BTree.BTreeTuple<K, V> tuple) throws IOException;
/**
* Get the previous tuple.
*
* @param tuple Tuple into which values are copied.
* @return True if values have been copied in tuple, or false if there is no previous tuple.
*/
boolean getPrevious(BTree.BTreeTuple<K, V> tuple) throws IOException;
/**
* Remove an entry with given key, and increases browsers expectedModCount
* This method is here to support 'ConcurrentModificationException' on Map interface.
*
* @param key
*/
void remove(K key) throws IOException;
}
/**
* Tuple consisting of a key-value pair.
*/
static final class BTreeTuple<K, V> {
K key;
V value;
BTreeTuple() {
// empty
}
BTreeTuple(K key, V value) {
this.key = key;
this.value = value;
}
}
}

View File

@ -1,97 +0,0 @@
package org.apache.jdbm;
import java.io.*;
/**
* An record lazily loaded from store.
* This is used in BTree/HTree to store big records outside of index tree
*
* @author Jan Kotek
*/
class BTreeLazyRecord<E> {
private E value = null;
private DBAbstract db;
private Serializer<E> serializer;
final long recid;
BTreeLazyRecord(DBAbstract db, long recid, Serializer<E> serializer) {
this.db = db;
this.recid = recid;
this.serializer = serializer;
}
E get() {
if (value != null) return value;
try {
value = db.fetch(recid, serializer);
} catch (IOException e) {
throw new IOError(e);
}
return value;
}
void delete() {
try {
db.delete(recid);
} catch (IOException e) {
throw new IOError(e);
}
value = null;
serializer = null;
db = null;
}
/**
* Serialier used to insert already serialized data into store
*/
static final Serializer FAKE_SERIALIZER = new Serializer() {
public void serialize(DataOutput out, Object obj) throws IOException {
byte[] data = (byte[]) obj;
out.write(data);
}
public Object deserialize(DataInput in) throws IOException, ClassNotFoundException {
throw new UnsupportedOperationException();
}
};
static Object fastDeser(DataInputOutput in, Serializer serializer, int expectedSize) throws IOException, ClassNotFoundException {
//we should propably copy data for deserialization into separate buffer and pass it to Serializer
//but to make it faster, Serializer will operate directly on top of buffer.
//and we check that it readed correct number of bytes.
int origAvail = in.available();
if (origAvail == 0)
throw new InternalError(); //is backed up by byte[] buffer, so there should be always avail bytes
Object ret = serializer.deserialize(in);
//check than valueSerializer did not read more bytes, if yes it readed bytes from next record
int readed = origAvail - in.available();
if (readed > expectedSize)
throw new IOException("Serializer readed more bytes than is record size.");
else if (readed != expectedSize) {
//deserializer did not readed all bytes, unussual but valid.
//Skip some to get into correct position
for (int ii = 0; ii < expectedSize - readed; ii++)
in.readUnsignedByte();
}
return ret;
}
/**
* if value in tree is serialized in more bytes, it is stored as separate record outside of tree
* This value must be always smaller than 250
*/
static final int MAX_INTREE_RECORD_SIZE = 32;
static {
if (MAX_INTREE_RECORD_SIZE > 250) throw new Error();
}
static final int NULL = 255;
static final int LAZY_RECORD = 254;
}

View File

@ -1,611 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOError;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentNavigableMap;
/**
* Wrapper for <code>BTree</code> which implements <code>ConcurrentNavigableMap</code> interface
*
* @param <K> key type
* @param <V> value type
*
* @author Jan Kotek
*/
class BTreeMap<K, V> extends AbstractMap<K, V> implements ConcurrentNavigableMap<K, V> {
protected BTree<K, V> tree;
protected final K fromKey;
protected final K toKey;
protected final boolean readonly;
protected NavigableSet<K> keySet2;
private final boolean toInclusive;
private final boolean fromInclusive;
public BTreeMap(BTree<K, V> tree, boolean readonly) {
this(tree, readonly, null, false, null, false);
}
protected BTreeMap(BTree<K, V> tree, boolean readonly, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
this.tree = tree;
this.fromKey = fromKey;
this.fromInclusive = fromInclusive;
this.toKey = toKey;
this.toInclusive = toInclusive;
this.readonly = readonly;
}
@Override
public Set<Entry<K, V>> entrySet() {
return _entrySet;
}
private final Set<java.util.Map.Entry<K, V>> _entrySet = new AbstractSet<Entry<K, V>>() {
protected Entry<K, V> newEntry(K k, V v) {
return new SimpleEntry<K, V>(k, v) {
private static final long serialVersionUID = 978651696969194154L;
public V setValue(V arg0) {
BTreeMap.this.put(getKey(), arg0);
return super.setValue(arg0);
}
};
}
public boolean add(java.util.Map.Entry<K, V> e) {
if (readonly)
throw new UnsupportedOperationException("readonly");
try {
if (e.getKey() == null)
throw new NullPointerException("Can not add null key");
if (!inBounds(e.getKey()))
throw new IllegalArgumentException("key outside of bounds");
return tree.insert(e.getKey(), e.getValue(), true) == null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
@SuppressWarnings("unchecked")
public boolean contains(Object o) {
if (o instanceof Entry) {
Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
try {
if (!inBounds(e.getKey()))
return false;
if (e.getKey() != null && tree.get(e.getKey()) != null)
return true;
} catch (IOException e1) {
throw new IOError(e1);
}
}
return false;
}
public Iterator<java.util.Map.Entry<K, V>> iterator() {
try {
final BTree.BTreeTupleBrowser<K, V> br = fromKey == null ?
tree.browse() : tree.browse(fromKey, fromInclusive);
return new Iterator<Entry<K, V>>() {
private Entry<K, V> next;
private K lastKey;
void ensureNext() {
try {
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
if (br.getNext(t) && inBounds(t.key))
next = newEntry(t.key, t.value);
else
next = null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
{
ensureNext();
}
public boolean hasNext() {
return next != null;
}
public java.util.Map.Entry<K, V> next() {
if (next == null)
throw new NoSuchElementException();
Entry<K, V> ret = next;
lastKey = ret.getKey();
//move to next position
ensureNext();
return ret;
}
public void remove() {
if (readonly)
throw new UnsupportedOperationException("readonly");
if (lastKey == null)
throw new IllegalStateException();
try {
br.remove(lastKey);
lastKey = null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
};
} catch (IOException e) {
throw new IOError(e);
}
}
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
if (readonly)
throw new UnsupportedOperationException("readonly");
if (o instanceof Entry) {
Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
try {
//check for nulls
if (e.getKey() == null || e.getValue() == null)
return false;
if (!inBounds(e.getKey()))
throw new IllegalArgumentException("out of bounds");
//get old value, must be same as item in entry
V v = get(e.getKey());
if (v == null || !e.getValue().equals(v))
return false;
V v2 = tree.remove(e.getKey());
return v2 != null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
return false;
}
public int size() {
return BTreeMap.this.size();
}
public void clear(){
if(fromKey!=null || toKey!=null)
super.clear();
else
try {
tree.clear();
} catch (IOException e) {
throw new IOError(e);
}
}
};
public boolean inBounds(K e) {
if(fromKey == null && toKey == null)
return true;
Comparator comp = comparator();
if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
if(fromKey!=null){
final int compare = comp.compare(e, fromKey);
if(compare<0) return false;
if(!fromInclusive && compare == 0) return false;
}
if(toKey!=null){
final int compare = comp.compare(e, toKey);
if(compare>0)return false;
if(!toInclusive && compare == 0) return false;
}
return true;
}
@SuppressWarnings("unchecked")
@Override
public V get(Object key) {
try {
if (key == null)
return null;
if (!inBounds((K) key))
return null;
return tree.get((K) key);
} catch (ClassCastException e) {
return null;
} catch (IOException e) {
throw new IOError(e);
}
}
@SuppressWarnings("unchecked")
@Override
public V remove(Object key) {
if (readonly)
throw new UnsupportedOperationException("readonly");
try {
if (key == null || tree.get((K) key) == null)
return null;
if (!inBounds((K) key))
throw new IllegalArgumentException("out of bounds");
return tree.remove((K) key);
} catch (ClassCastException e) {
return null;
} catch (IOException e) {
throw new IOError(e);
}
}
public V put(K key, V value) {
if (readonly)
throw new UnsupportedOperationException("readonly");
try {
if (key == null || value == null)
throw new NullPointerException("Null key or value");
if (!inBounds(key))
throw new IllegalArgumentException("out of bounds");
return tree.insert(key, value, true);
} catch (IOException e) {
throw new IOError(e);
}
}
public void clear(){
entrySet().clear();
}
@SuppressWarnings("unchecked")
@Override
public boolean containsKey(Object key) {
if (key == null)
return false;
try {
if (!inBounds((K) key))
return false;
V v = tree.get((K) key);
return v != null;
} catch (IOException e) {
throw new IOError(e);
} catch (ClassCastException e) {
return false;
}
}
public Comparator<? super K> comparator() {
return tree._comparator;
}
public K firstKey() {
if (isEmpty())
return null;
try {
BTree.BTreeTupleBrowser<K, V> b = fromKey == null ? tree.browse() : tree.browse(fromKey,fromInclusive);
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getNext(t);
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public K lastKey() {
if (isEmpty())
return null;
try {
BTree.BTreeTupleBrowser<K, V> b = toKey == null ? tree.browse(null,true) : tree.browse(toKey,false);
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getPrevious(t);
if(!toInclusive && toKey!=null){
//make sure we wont return last key
Comparator c = comparator();
if(c==null) c=Utils.COMPARABLE_COMPARATOR;
if(c.compare(t.key,toKey)==0)
b.getPrevious(t);
}
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public ConcurrentNavigableMap<K, V> headMap(K toKey2, boolean inclusive) {
K toKey3 = Utils.min(this.toKey,toKey2,comparator());
boolean inclusive2 = toKey3 == toKey? toInclusive : inclusive;
return new BTreeMap<K, V>(tree, readonly, this.fromKey, this.fromInclusive, toKey3, inclusive2);
}
public ConcurrentNavigableMap<K, V> headMap(K toKey) {
return headMap(toKey,false);
}
public Entry<K, V> lowerEntry(K key) {
K k = lowerKey(key);
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public K lowerKey(K key) {
if (isEmpty())
return null;
K key2 = Utils.min(key,toKey,comparator());
try {
BTree.BTreeTupleBrowser<K, V> b = tree.browse(key2,true) ;
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getPrevious(t);
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public Entry<K, V> floorEntry(K key) {
K k = floorKey(key);
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public K floorKey(K key) {
if (isEmpty())
return null;
K key2 = Utils.max(key,fromKey,comparator());
try {
BTree.BTreeTupleBrowser<K, V> b = tree.browse(key2,true) ;
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getNext(t);
Comparator comp = comparator();
if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
if(comp.compare(t.key,key2) == 0)
return t.key;
b.getPrevious(t);
b.getPrevious(t);
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public Entry<K, V> ceilingEntry(K key) {
K k = ceilingKey(key);
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public K ceilingKey(K key) {
if (isEmpty())
return null;
K key2 = Utils.min(key,toKey,comparator());
try {
BTree.BTreeTupleBrowser<K, V> b = tree.browse(key2,true) ;
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getNext(t);
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public Entry<K, V> higherEntry(K key) {
K k = higherKey(key);
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public K higherKey(K key) {
if (isEmpty())
return null;
K key2 = Utils.max(key,fromKey,comparator());
try {
BTree.BTreeTupleBrowser<K, V> b = tree.browse(key2,false) ;
BTree.BTreeTuple<K, V> t = new BTree.BTreeTuple<K, V>();
b.getNext(t);
return t.key;
} catch (IOException e) {
throw new IOError(e);
}
}
public Entry<K, V> firstEntry() {
K k = firstKey();
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public Entry<K, V> lastEntry() {
K k = lastKey();
return k==null? null : new SimpleEntry<K, V>(k,get(k));
}
public Entry<K, V> pollFirstEntry() {
Entry<K,V> first = firstEntry();
if(first!=null)
remove(first.getKey());
return first;
}
public Entry<K, V> pollLastEntry() {
Entry<K,V> last = lastEntry();
if(last!=null)
remove(last.getKey());
return last;
}
public ConcurrentNavigableMap<K, V> descendingMap() {
throw new UnsupportedOperationException("not implemented yet");
//TODO implement descending (reverse order) map
}
public NavigableSet<K> keySet() {
return navigableKeySet();
}
public NavigableSet<K> navigableKeySet() {
if(keySet2 == null)
keySet2 = new BTreeSet<K>((BTreeMap<K,Object>) this);
return keySet2;
}
public NavigableSet<K> descendingKeySet() {
return descendingMap().navigableKeySet();
}
public ConcurrentNavigableMap<K, V> tailMap(K fromKey) {
return tailMap(fromKey,true);
}
public ConcurrentNavigableMap<K, V> tailMap(K fromKey2, boolean inclusive) {
K fromKey3 = Utils.max(this.fromKey,fromKey2,comparator());
boolean inclusive2 = fromKey3 == toKey? toInclusive : inclusive;
return new BTreeMap<K, V>(tree, readonly, fromKey3, inclusive2, toKey, toInclusive);
}
public ConcurrentNavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
Comparator comp = comparator();
if (comp == null) comp = Utils.COMPARABLE_COMPARATOR;
if (comp.compare(fromKey, toKey) > 0)
throw new IllegalArgumentException("fromKey is bigger then toKey");
return new BTreeMap<K, V>(tree, readonly, fromKey, fromInclusive, toKey, toInclusive);
}
public ConcurrentNavigableMap<K, V> subMap(K fromKey, K toKey) {
return subMap(fromKey,true,toKey,false);
}
public BTree<K, V> getTree() {
return tree;
}
public void addRecordListener(RecordListener<K, V> listener) {
tree.addRecordListener(listener);
}
public DBAbstract getRecordManager() {
return tree.getRecordManager();
}
public void removeRecordListener(RecordListener<K, V> listener) {
tree.removeRecordListener(listener);
}
public int size() {
if (fromKey == null && toKey == null)
return (int) tree._entries; //use fast counter on tree if Map has no bounds
else {
//had to count items in iterator
Iterator iter = keySet().iterator();
int counter = 0;
while (iter.hasNext()) {
iter.next();
counter++;
}
return counter;
}
}
public V putIfAbsent(K key, V value) {
tree.lock.writeLock().lock();
try{
if (!containsKey(key))
return put(key, value);
else
return get(key);
}finally {
tree.lock.writeLock().unlock();
}
}
public boolean remove(Object key, Object value) {
tree.lock.writeLock().lock();
try{
if (containsKey(key) && get(key).equals(value)) {
remove(key);
return true;
} else return false;
}finally {
tree.lock.writeLock().unlock();
}
}
public boolean replace(K key, V oldValue, V newValue) {
tree.lock.writeLock().lock();
try{
if (containsKey(key) && get(key).equals(oldValue)) {
put(key, newValue);
return true;
} else return false;
}finally {
tree.lock.writeLock().unlock();
}
}
public V replace(K key, V value) {
tree.lock.writeLock().lock();
try{
if (containsKey(key)) {
return put(key, value);
} else return null;
}finally {
tree.lock.writeLock().unlock();
}
}
}

View File

@ -1,187 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdbm;
import java.util.*;
/**
* Wrapper class for <code>>SortedMap</code> to implement <code>>NavigableSet</code>
* <p/>
* This code originally comes from Apache Harmony, was adapted by Jan Kotek for JDBM
*/
class BTreeSet<E> extends AbstractSet<E> implements NavigableSet<E> {
/**
* use keyset from this map
*/
final BTreeMap<E, Object> map;
BTreeSet(BTreeMap<E, Object> map) {
this.map = map;
}
public boolean add(E object) {
return map.put(object, Utils.EMPTY_STRING) == null;
}
public boolean addAll(Collection<? extends E> collection) {
return super.addAll(collection);
}
public void clear() {
map.clear();
}
public Comparator<? super E> comparator() {
return map.comparator();
}
public boolean contains(Object object) {
return map.containsKey(object);
}
public boolean isEmpty() {
return map.isEmpty();
}
public E lower(E e) {
return map.lowerKey(e);
}
public E floor(E e) {
return map.floorKey(e);
}
public E ceiling(E e) {
return map.ceilingKey(e);
}
public E higher(E e) {
return map.higherKey(e);
}
public E pollFirst() {
Map.Entry<E,Object> e = map.pollFirstEntry();
return e!=null? e.getKey():null;
}
public E pollLast() {
Map.Entry<E,Object> e = map.pollLastEntry();
return e!=null? e.getKey():null;
}
public Iterator<E> iterator() {
final Iterator<Map.Entry<E,Object>> iter = map.entrySet().iterator();
return new Iterator<E>() {
public boolean hasNext() {
return iter.hasNext();
}
public E next() {
Map.Entry<E,Object> e = iter.next();
return e!=null?e.getKey():null;
}
public void remove() {
iter.remove();
}
};
}
public NavigableSet<E> descendingSet() {
return map.descendingKeySet();
}
public Iterator<E> descendingIterator() {
return map.descendingKeySet().iterator();
}
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
return map.subMap(fromElement,fromInclusive,toElement,toInclusive).navigableKeySet();
}
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return map.headMap(toElement,inclusive).navigableKeySet();
}
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return map.tailMap(fromElement,inclusive).navigableKeySet();
}
public boolean remove(Object object) {
return map.remove(object) != null;
}
public int size() {
return map.size();
}
public E first() {
return map.firstKey();
}
public E last() {
return map.lastKey();
}
public SortedSet<E> subSet(E start, E end) {
Comparator<? super E> c = map.comparator();
int compare = (c == null) ? ((Comparable<E>) start).compareTo(end) : c
.compare(start, end);
if (compare <= 0) {
return new BTreeSet<E>((BTreeMap<E,Object>) map.subMap(start, true,end,false));
}
throw new IllegalArgumentException();
}
public SortedSet<E> headSet(E end) {
// Check for errors
Comparator<? super E> c = map.comparator();
if (c == null) {
((Comparable<E>) end).compareTo(end);
} else {
c.compare(end, end);
}
return new BTreeSet<E>((BTreeMap<E,Object>) map.headMap(end,false));
}
public SortedSet<E> tailSet(E start) {
// Check for errors
Comparator<? super E> c = map.comparator();
if (c == null) {
((Comparable<E>) start).compareTo(start);
} else {
c.compare(start, start);
}
return new BTreeSet<E>((BTreeMap<E,Object>) map.tailMap(start,true));
}
}

View File

@ -1,173 +0,0 @@
package org.apache.jdbm;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
/**
* Database is root class for creating and loading persistent collections. It also contains
* transaction operations.
* //TODO just write some readme
* <p/>
*
* @author Jan Kotek
* @author Alex Boisvert
* @author Cees de Groot
*/
public interface DB {
/**
* Closes the DB and release resources.
* DB can not be used after it was closed
*/
void close();
/** @return true if db was already closed */
boolean isClosed();
/**
* Clear cache and remove all entries it contains.
* This may be useful for some Garbage Collection when reference cache is used.
*/
void clearCache();
/**
* Defragments storage so it consumes less space.
* It basically copyes all records into different store and then renames it, replacing original store.
* <p/>
* Defrag has two steps: In first collections are rearranged, so records in collection are close to each other,
* and read speed is improved. In second step all records are sequentially transferred, reclaiming all unused space.
* First step is optinal and may slow down defragmentation significantly as ut requires many random-access reads.
* Second step reads and writes data sequentially and is very fast, comparable to copying files to new location.
*
* <p/>
* This commits any uncommited data. Defrag also requires free space, as store is basically recreated at new location.
*
* @param sortCollections if collection records should be rearranged during defragment, this takes some extra time
*/
void defrag(boolean sortCollections);
/**
* Commit (make persistent) all changes since beginning of transaction.
* JDBM supports only single transaction.
*/
void commit();
/**
* Rollback (cancel) all changes since beginning of transaction.
* JDBM supports only single transaction.
* This operations affects all maps created or loaded by this DB.
*/
void rollback();
/**
* This calculates some database statistics such as collection sizes and record distributions.
* Can be useful for performance optimalisations and trouble shuting.
* This method can run for very long time.
*
* @return statistics contained in string
*/
String calculateStatistics();
/**
* Copy database content into ZIP file
* @param zipFile
*/
void copyToZip(String zipFile);
/**
* Get a <code>Map</code> which was already created and saved in DB.
* This map uses disk based H*Tree and should have similar performance
* as <code>HashMap</code>.
*
* @param name of hash map
*
* @return map
*/
<K, V> ConcurrentMap<K, V> getHashMap(String name);
/**
* Creates Map which persists data into DB.
*
* @param name record name
* @return
*/
<K, V> ConcurrentMap<K, V> createHashMap(String name);
/**
* Creates Hash Map which persists data into DB.
* Map will use custom serializers for Keys and Values.
* Leave keySerializer null to use default serializer for keys
*
* @param <K> Key type
* @param <V> Value type
* @param name record name
* @param keySerializer serializer to be used for Keys, leave null to use default serializer
* @param valueSerializer serializer to be used for Values
* @return
*/
<K, V> ConcurrentMap<K, V> createHashMap(String name, Serializer<K> keySerializer, Serializer<V> valueSerializer);
<K> Set<K> createHashSet(String name);
<K> Set<K> getHashSet(String name);
<K> Set<K> createHashSet(String name, Serializer<K> keySerializer);
<K, V> ConcurrentNavigableMap<K, V> getTreeMap(String name);
/**
* Create TreeMap which persists data into DB.
*
* @param <K> Key type
* @param <V> Value type
* @param name record name
* @return
*/
<K extends Comparable, V> NavigableMap<K, V> createTreeMap(String name);
/**
* Creates TreeMap which persists data into DB.
*
* @param <K> Key type
* @param <V> Value type
* @param name record name
* @param keyComparator Comparator used to sort keys
* @param keySerializer Serializer used for keys. This may reduce disk space usage *
* @param valueSerializer Serializer used for values. This may reduce disk space usage
* @return
*/
<K, V> ConcurrentNavigableMap<K, V> createTreeMap(String name,
Comparator<K> keyComparator, Serializer<K> keySerializer, Serializer<V> valueSerializer);
<K> NavigableSet<K> getTreeSet(String name);
<K> NavigableSet<K> createTreeSet(String name);
<K> NavigableSet<K> createTreeSet(String name, Comparator<K> keyComparator, Serializer<K> keySerializer);
<K> List<K> createLinkedList(String name);
<K> List<K> createLinkedList(String name, Serializer<K> serializer);
<K> List<K> getLinkedList(String name);
/** returns unmodifiable map which contains all collection names and collections thenselfs*/
Map<String,Object> getCollections();
/** completely remove collection from store*/
void deleteCollection(String name);
/** Java Collections returns their size as int. This may not be enought for JDBM collections.
* This method returns number of elements in JDBM collection as long.
*
* @param collection created by JDBM
* @return number of elements in collection as long
*/
long collectionSize(Object collection);
}

View File

@ -1,590 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOError;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
/**
* An abstract class implementing most of DB.
* It also has some JDBM package protected stuff (getNamedRecord)
*/
abstract class DBAbstract implements DB {
/**
* Reserved slot for name directory recid.
*/
static final byte NAME_DIRECTORY_ROOT = 0;
/**
* Reserved slot for version number
*/
static final byte STORE_VERSION_NUMBER_ROOT = 1;
/**
* Reserved slot for recid where Serial class info is stored
*
* NOTE when introducing more roots, do not forget to update defrag
*/
static final byte SERIAL_CLASS_INFO_RECID_ROOT = 2;
/** to prevent double instances of the same collection, we use weak value map
*
* //TODO what to do when there is rollback?
* //TODO clear on close
*/
final private Map<String,WeakReference<Object>> collections = new HashMap<String,WeakReference<Object>>();
/**
* Inserts a new record using a custom serializer.
*
* @param obj the object for the new record.
* @param serializer a custom serializer
* @return the rowid for the new record.
* @throws java.io.IOException when one of the underlying I/O operations fails.
*/
abstract <A> long insert(A obj, Serializer<A> serializer,boolean disableCache) throws IOException;
/**
* Deletes a record.
*
* @param recid the rowid for the record that should be deleted.
* @throws java.io.IOException when one of the underlying I/O operations fails.
*/
abstract void delete(long recid) throws IOException;
/**
* Updates a record using a custom serializer.
* If given recid does not exist, IOException will be thrown before/during commit (cache).
*
* @param recid the recid for the record that is to be updated.
* @param obj the new object for the record.
* @param serializer a custom serializer
* @throws java.io.IOException when one of the underlying I/O operations fails
*/
abstract <A> void update(long recid, A obj, Serializer<A> serializer)
throws IOException;
/**
* Fetches a record using a custom serializer.
*
* @param recid the recid for the record that must be fetched.
* @param serializer a custom serializer
* @return the object contained in the record, null if given recid does not exist
* @throws java.io.IOException when one of the underlying I/O operations fails.
*/
abstract <A> A fetch(long recid, Serializer<A> serializer)
throws IOException;
/**
* Fetches a record using a custom serializer and optionaly disabled cache
*
* @param recid the recid for the record that must be fetched.
* @param serializer a custom serializer
* @param disableCache true to disable any caching mechanism
* @return the object contained in the record, null if given recid does not exist
* @throws java.io.IOException when one of the underlying I/O operations fails.
*/
abstract <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache)
throws IOException;
public long insert(Object obj) throws IOException {
return insert(obj, defaultSerializer(),false);
}
public void update(long recid, Object obj) throws IOException {
update(recid, obj, defaultSerializer());
}
synchronized public <A> A fetch(long recid) throws IOException {
return (A) fetch(recid, defaultSerializer());
}
synchronized public <K, V> ConcurrentMap<K, V> getHashMap(String name) {
Object o = getCollectionInstance(name);
if(o!=null)
return (ConcurrentMap<K, V>) o;
try {
long recid = getNamedObject(name);
if(recid == 0) return null;
HTree tree = fetch(recid);
tree.setPersistenceContext(this);
if(!tree.hasValues()){
throw new ClassCastException("HashSet is not HashMap");
}
collections.put(name,new WeakReference<Object>(tree));
return tree;
} catch (IOException e) {
throw new IOError(e);
}
}
synchronized public <K, V> ConcurrentMap<K, V> createHashMap(String name) {
return createHashMap(name, null, null);
}
public synchronized <K, V> ConcurrentMap<K, V> createHashMap(String name, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
try {
assertNameNotExist(name);
HTree<K, V> tree = new HTree(this, keySerializer, valueSerializer,true);
long recid = insert(tree);
setNamedObject(name, recid);
collections.put(name,new WeakReference<Object>(tree));
return tree;
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized <K> Set<K> getHashSet(String name) {
Object o = getCollectionInstance(name);
if(o!=null)
return (Set<K>) o;
try {
long recid = getNamedObject(name);
if(recid == 0) return null;
HTree tree = fetch(recid);
tree.setPersistenceContext(this);
if(tree.hasValues()){
throw new ClassCastException("HashMap is not HashSet");
}
Set<K> ret = new HTreeSet(tree);
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized <K> Set<K> createHashSet(String name) {
return createHashSet(name, null);
}
public synchronized <K> Set<K> createHashSet(String name, Serializer<K> keySerializer) {
try {
assertNameNotExist(name);
HTree<K, Object> tree = new HTree(this, keySerializer, null,false);
long recid = insert(tree);
setNamedObject(name, recid);
Set<K> ret = new HTreeSet<K>(tree);
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
synchronized public <K, V> ConcurrentNavigableMap<K, V> getTreeMap(String name) {
Object o = getCollectionInstance(name);
if(o!=null)
return (ConcurrentNavigableMap<K, V> ) o;
try {
long recid = getNamedObject(name);
if(recid == 0) return null;
BTree t = BTree.<K, V>load(this, recid);
if(!t.hasValues())
throw new ClassCastException("TreeSet is not TreeMap");
ConcurrentNavigableMap<K,V> ret = new BTreeMap<K, V>(t,false); //TODO put readonly flag here
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
synchronized public <K extends Comparable, V> ConcurrentNavigableMap<K, V> createTreeMap(String name) {
return createTreeMap(name, null, null, null);
}
public synchronized <K, V> ConcurrentNavigableMap<K, V> createTreeMap(String name,
Comparator<K> keyComparator,
Serializer<K> keySerializer,
Serializer<V> valueSerializer) {
try {
assertNameNotExist(name);
BTree<K, V> tree = BTree.createInstance(this, keyComparator, keySerializer, valueSerializer,true);
setNamedObject(name, tree.getRecid());
ConcurrentNavigableMap<K,V> ret = new BTreeMap<K, V>(tree,false); //TODO put readonly flag here
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized <K> NavigableSet<K> getTreeSet(String name) {
Object o = getCollectionInstance(name);
if(o!=null)
return (NavigableSet<K> ) o;
try {
long recid = getNamedObject(name);
if(recid == 0) return null;
BTree t = BTree.<K, Object>load(this, recid);
if(t.hasValues())
throw new ClassCastException("TreeMap is not TreeSet");
BTreeSet<K> ret = new BTreeSet<K>(new BTreeMap(t,false));
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized <K> NavigableSet<K> createTreeSet(String name) {
return createTreeSet(name, null, null);
}
public synchronized <K> NavigableSet<K> createTreeSet(String name, Comparator<K> keyComparator, Serializer<K> keySerializer) {
try {
assertNameNotExist(name);
BTree<K, Object> tree = BTree.createInstance(this, keyComparator, keySerializer, null,false);
setNamedObject(name, tree.getRecid());
BTreeSet<K> ret = new BTreeSet<K>(new BTreeMap(tree,false));
collections.put(name,new WeakReference<Object>(ret));
return ret;
} catch (IOException e) {
throw new IOError(e);
}
}
synchronized public <K> List<K> createLinkedList(String name) {
return createLinkedList(name, null);
}
synchronized public <K> List<K> createLinkedList(String name, Serializer<K> serializer) {
try {
assertNameNotExist(name);
//allocate record and overwrite it
LinkedList2<K> list = new LinkedList2<K>(this, serializer);
long recid = insert(list);
setNamedObject(name, recid);
collections.put(name,new WeakReference<Object>(list));
return list;
} catch (IOException e) {
throw new IOError(e);
}
}
synchronized public <K> List<K> getLinkedList(String name) {
Object o = getCollectionInstance(name);
if(o!=null)
return (List<K> ) o;
try {
long recid = getNamedObject(name);
if(recid == 0) return null;
LinkedList2<K> list = (LinkedList2<K>) fetch(recid);
list.setPersistenceContext(this);
collections.put(name,new WeakReference<Object>(list));
return list;
} catch (IOException e) {
throw new IOError(e);
}
}
private synchronized Object getCollectionInstance(String name){
WeakReference ref = collections.get(name);
if(ref==null)return null;
Object o = ref.get();
if(o != null) return o;
//already GCed
collections.remove(name);
return null;
}
private void assertNameNotExist(String name) throws IOException {
if (getNamedObject(name) != 0)
throw new IllegalArgumentException("Object with name '" + name + "' already exists");
}
/**
* Obtain the record id of a named object. Returns 0 if named object
* doesn't exist.
* Named objects are used to store Map views and other well known objects.
*/
synchronized protected long getNamedObject(String name) throws IOException{
long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
if(nameDirectory_recid == 0){
return 0;
}
HTree<String,Long> m = fetch(nameDirectory_recid);
Long res = m.get(name);
if(res == null)
return 0;
return res;
}
/**
* Set the record id of a named object.
* Named objects are used to store Map views and other well known objects.
*/
synchronized protected void setNamedObject(String name, long recid) throws IOException{
long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
HTree<String,Long> m = null;
if(nameDirectory_recid == 0){
//does not exists, create it
m = new HTree<String, Long>(this,null,null,true);
nameDirectory_recid = insert(m);
setRoot(NAME_DIRECTORY_ROOT,nameDirectory_recid);
}else{
//fetch it
m = fetch(nameDirectory_recid);
}
m.put(name,recid);
}
synchronized public Map<String,Object> getCollections(){
try{
Map<String,Object> ret = new LinkedHashMap<String, Object>();
long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
if(nameDirectory_recid==0)
return ret;
HTree<String,Long> m = fetch(nameDirectory_recid);
for(Map.Entry<String,Long> e:m.entrySet()){
Object o = fetch(e.getValue());
if(o instanceof BTree){
if(((BTree) o).hasValues)
o = getTreeMap(e.getKey());
else
o = getTreeSet(e.getKey());
}
else if( o instanceof HTree){
if(((HTree) o).hasValues)
o = getHashMap(e.getKey());
else
o = getHashSet(e.getKey());
}
ret.put(e.getKey(), o);
}
return Collections.unmodifiableMap(ret);
}catch(IOException e){
throw new IOError(e);
}
}
synchronized public void deleteCollection(String name){
try{
long nameDirectory_recid = getRoot(NAME_DIRECTORY_ROOT);
if(nameDirectory_recid==0)
throw new IOException("Collection not found");
HTree<String,Long> dir = fetch(nameDirectory_recid);
Long recid = dir.get(name);
if(recid == null) throw new IOException("Collection not found");
Object o = fetch(recid);
//we can not use O instance since it is not correctly initialized
if(o instanceof LinkedList2){
LinkedList2 l = (LinkedList2) o;
l.clear();
delete(l.rootRecid);
}else if(o instanceof BTree){
((BTree) o).clear();
} else if( o instanceof HTree){
HTree t = (HTree) o;
t.clear();
HTreeDirectory n = (HTreeDirectory) fetch(t.rootRecid,t.SERIALIZER);
n.deleteAllChildren();
delete(t.rootRecid);
}else{
throw new InternalError("unknown collection type: "+(o==null?null:o.getClass()));
}
delete(recid);
collections.remove(name);
dir.remove(name);
}catch(IOException e){
throw new IOError(e);
}
}
/** we need to set reference to this DB instance, so serializer needs to be here*/
final Serializer<Serialization> defaultSerializationSerializer = new Serializer<Serialization>(){
public void serialize(DataOutput out, Serialization obj) throws IOException {
LongPacker.packLong(out,obj.serialClassInfoRecid);
SerialClassInfo.serializer.serialize(out,obj.registered);
}
public Serialization deserialize(DataInput in) throws IOException, ClassNotFoundException {
final long recid = LongPacker.unpackLong(in);
final ArrayList<SerialClassInfo.ClassInfo> classes = SerialClassInfo.serializer.deserialize(in);
return new Serialization(DBAbstract.this,recid,classes);
}
};
public synchronized Serializer defaultSerializer() {
try{
long serialClassInfoRecid = getRoot(SERIAL_CLASS_INFO_RECID_ROOT);
if (serialClassInfoRecid == 0) {
//allocate new recid
serialClassInfoRecid = insert(null,Utils.NULL_SERIALIZER,false);
//and insert new serializer
Serialization ser = new Serialization(this,serialClassInfoRecid,new ArrayList<SerialClassInfo.ClassInfo>());
update(serialClassInfoRecid,ser, defaultSerializationSerializer);
setRoot(SERIAL_CLASS_INFO_RECID_ROOT, serialClassInfoRecid);
return ser;
}else{
return fetch(serialClassInfoRecid,defaultSerializationSerializer);
}
} catch (IOException e) {
throw new IOError(e);
}
}
final protected void checkNotClosed(){
if(isClosed()) throw new IllegalStateException("db was closed");
}
protected abstract void setRoot(byte root, long recid);
protected abstract long getRoot(byte root);
synchronized public long collectionSize(Object collection){
if(collection instanceof BTreeMap){
BTreeMap t = (BTreeMap) collection;
if(t.fromKey!=null|| t.toKey!=null) throw new IllegalArgumentException("collectionSize does not work on BTree submap");
return t.tree._entries;
}else if(collection instanceof HTree){
return ((HTree)collection).getRoot().size;
}else if(collection instanceof HTreeSet){
return collectionSize(((HTreeSet) collection).map);
}else if(collection instanceof BTreeSet){
return collectionSize(((BTreeSet) collection).map);
}else if(collection instanceof LinkedList2){
return ((LinkedList2)collection).getRoot().size;
}else{
throw new IllegalArgumentException("Not JDBM collection");
}
}
void addShutdownHook(){
if(shutdownCloseThread!=null){
shutdownCloseThread = new ShutdownCloseThread();
Runtime.getRuntime().addShutdownHook(shutdownCloseThread);
}
}
public void close(){
if(shutdownCloseThread!=null){
Runtime.getRuntime().removeShutdownHook(shutdownCloseThread);
shutdownCloseThread.dbToClose = null;
shutdownCloseThread = null;
}
}
ShutdownCloseThread shutdownCloseThread = null;
private static class ShutdownCloseThread extends Thread{
DBAbstract dbToClose = null;
ShutdownCloseThread(){
super("JDBM shutdown");
}
public void run(){
if(dbToClose!=null && !dbToClose.isClosed()){
dbToClose.shutdownCloseThread = null;
dbToClose.close();
}
}
}
synchronized public void rollback() {
try {
for(WeakReference<Object> o:collections.values()){
Object c = o.get();
if(c != null && c instanceof BTreeMap){
//reload tree
BTreeMap m = (BTreeMap) c;
m.tree = fetch(m.tree.getRecid());
}
if(c != null && c instanceof BTreeSet){
//reload tree
BTreeSet m = (BTreeSet) c;
m.map.tree = fetch(m.map.tree.getRecid());
}
}
} catch (IOException e) {
throw new IOError(e);
}
}
}

View File

@ -1,162 +0,0 @@
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.IOError;
import java.io.IOException;
import java.util.Comparator;
import java.util.Iterator;
/**
* Abstract class with common cache functionality
*/
abstract class DBCache extends DBStore{
static final int NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT = 1024;
static final byte NONE = 1;
static final byte MRU = 2;
static final byte WEAK = 3;
static final byte SOFT = 4;
static final byte HARD = 5;
static final class DirtyCacheEntry {
long _recid; //TODO recid is already part of _hashDirties, so this field could be removed to save memory
Object _obj;
Serializer _serializer;
}
/**
* Dirty status of _hash CacheEntry Values
*/
final protected LongHashMap<DirtyCacheEntry> _hashDirties = new LongHashMap<DirtyCacheEntry>();
private Serializer cachedDefaultSerializer = null;
/**
* Construct a CacheRecordManager wrapping another DB and
* using a given cache policy.
*/
public DBCache(String filename, boolean readonly, boolean transactionDisabled,
Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
boolean deleteFilesAfterClose,boolean lockingDisabled){
super(filename, readonly, transactionDisabled,
cipherIn, cipherOut, useRandomAccessFile,
deleteFilesAfterClose,lockingDisabled);
}
@Override
public synchronized Serializer defaultSerializer(){
if(cachedDefaultSerializer==null)
cachedDefaultSerializer = super.defaultSerializer();
return cachedDefaultSerializer;
}
@Override
boolean needsAutoCommit() {
return super.needsAutoCommit()||
(transactionsDisabled && !commitInProgress && _hashDirties.size() > NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT);
}
public synchronized <A> long insert(final A obj, final Serializer<A> serializer, final boolean disableCache)
throws IOException {
checkNotClosed();
if(super.needsAutoCommit())
commit();
if(disableCache)
return super.insert(obj, serializer, disableCache);
//prealocate recid so we have something to return
final long recid = super.insert(PREALOCATE_OBJ, null, disableCache);
// super.update(recid, obj,serializer);
// return super.insert(obj,serializer,disableCache);
//and create new dirty record for future update
final DirtyCacheEntry e = new DirtyCacheEntry();
e._recid = recid;
e._obj = obj;
e._serializer = serializer;
_hashDirties.put(recid,e);
return recid;
}
public synchronized void commit() {
try{
commitInProgress = true;
updateCacheEntries();
super.commit();
}finally {
commitInProgress = false;
}
}
public synchronized void rollback(){
cachedDefaultSerializer = null;
_hashDirties.clear();
super.rollback();
}
private static final Comparator<DirtyCacheEntry> DIRTY_COMPARATOR = new Comparator<DirtyCacheEntry>() {
final public int compare(DirtyCacheEntry o1, DirtyCacheEntry o2) {
return (int) (o1._recid - o2._recid);
}
};
/**
* Update all dirty cache objects to the underlying DB.
*/
protected void updateCacheEntries() {
try {
synchronized(_hashDirties){
while(!_hashDirties.isEmpty()){
//make defensive copy of values as _db.update() may trigger changes in db
//and this would modify dirties again
DirtyCacheEntry[] vals = new DirtyCacheEntry[_hashDirties.size()];
Iterator<DirtyCacheEntry> iter = _hashDirties.valuesIterator();
for(int i = 0;i<vals.length;i++){
vals[i] = iter.next();
}
iter = null;
java.util.Arrays.sort(vals,DIRTY_COMPARATOR);
for(int i = 0;i<vals.length;i++){
final DirtyCacheEntry entry = vals[i];
vals[i] = null;
super.update(entry._recid, entry._obj, entry._serializer);
_hashDirties.remove(entry._recid);
}
//update may have triggered more records to be added into dirties, so repeat until all records are written.
}
}
} catch (IOException e) {
throw new IOError(e);
}
}
}

View File

@ -1,350 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.IOException;
/**
* A DB wrapping and caching another DB.
*
* @author Jan Kotek
* @author Alex Boisvert
* @author Cees de Groot
*
* TODO add 'cache miss' statistics
*/
class DBCacheMRU
extends DBCache {
private static final boolean debug = false;
/**
* Cached object hashtable
*/
protected LongHashMap<CacheEntry> _hash;
/**
* Maximum number of objects in the cache.
*/
protected int _max;
/**
* Beginning of linked-list of cache elements. First entry is element
* which has been used least recently.
*/
protected CacheEntry _first;
/**
* End of linked-list of cache elements. Last entry is element
* which has been used most recently.
*/
protected CacheEntry _last;
/**
* Construct a CacheRecordManager wrapping another DB and
* using a given cache policy.
*/
public DBCacheMRU(String filename, boolean readonly, boolean transactionDisabled,
Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
boolean deleteFilesAfterClose, int cacheMaxRecords, boolean lockingDisabled) {
super(filename, readonly, transactionDisabled,
cipherIn, cipherOut, useRandomAccessFile,
deleteFilesAfterClose,lockingDisabled);
_hash = new LongHashMap<CacheEntry>(cacheMaxRecords);
_max = cacheMaxRecords;
}
public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
if (disableCache)
return super.fetch(recid, serializer, disableCache);
else
return fetch(recid, serializer);
}
public synchronized void delete(long recid)
throws IOException {
checkNotClosed();
super.delete(recid);
synchronized (_hash){
CacheEntry entry = _hash.get(recid);
if (entry != null) {
removeEntry(entry);
_hash.remove(entry._recid);
}
_hashDirties.remove(recid);
}
if(super.needsAutoCommit())
commit();
}
public synchronized <A> void update(final long recid, final A obj, final Serializer<A> serializer) throws IOException {
checkNotClosed();
synchronized (_hash){
//remove entry if it already exists
CacheEntry entry = cacheGet(recid);
if (entry != null) {
_hash.remove(recid);
removeEntry(entry);
}
//check if entry is in dirties, in this case just update its object
DirtyCacheEntry e = _hashDirties.get(recid);
if(e!=null){
if(recid!=e._recid) throw new Error();
e._obj = obj;
e._serializer = serializer;
return;
}
//create new dirty entry
e = new DirtyCacheEntry();
e._recid = recid;
e._obj = obj;
e._serializer = serializer;
_hashDirties.put(recid,e);
}
if(super.needsAutoCommit())
commit();
}
public synchronized <A> A fetch(long recid, Serializer<A> serializer)
throws IOException {
checkNotClosed();
final CacheEntry entry = cacheGet(recid);
if (entry != null) {
return (A) entry._obj;
}
//check dirties
final DirtyCacheEntry entry2 = _hashDirties.get(recid);
if(entry2!=null){
return (A) entry2._obj;
}
A value = super.fetch(recid, serializer);
if(super.needsAutoCommit())
commit();
//put record into MRU cache
cachePut(recid, value);
return value;
}
public synchronized void close() {
if(isClosed())
return;
updateCacheEntries();
super.close();
_hash = null;
}
public synchronized void rollback() {
// discard all cache entries since we don't know which entries
// where part of the transaction
synchronized (_hash){
_hash.clear();
_first = null;
_last = null;
}
super.rollback();
}
/**
* Obtain an object in the cache
*/
protected CacheEntry cacheGet(long key) {
synchronized (_hash){
CacheEntry entry = _hash.get(key);
if ( entry != null && _last != entry) {
//touch entry
removeEntry(entry);
addEntry(entry);
}
return entry;
}
}
/**
* Place an object in the cache.
*
* @throws IOException
*/
protected void cachePut(final long recid, final Object value) throws IOException {
synchronized (_hash){
CacheEntry entry = _hash.get(recid);
if (entry != null) {
entry._obj = value;
//touch entry
if (_last != entry) {
removeEntry(entry);
addEntry(entry);
}
} else {
if (_hash.size() >= _max) {
// purge and recycle entry
entry = purgeEntry();
entry._recid = recid;
entry._obj = value;
} else {
entry = new CacheEntry(recid, value);
}
addEntry(entry);
_hash.put(entry._recid, entry);
}
}
}
/**
* Add a CacheEntry. Entry goes at the end of the list.
*/
protected void addEntry(CacheEntry entry) {
synchronized (_hash){
if (_first == null) {
_first = entry;
_last = entry;
} else {
_last._next = entry;
entry._previous = _last;
_last = entry;
}
}
}
/**
* Remove a CacheEntry from linked list
*/
protected void removeEntry(CacheEntry entry) {
synchronized (_hash){
if (entry == _first) {
_first = entry._next;
}
if (_last == entry) {
_last = entry._previous;
}
CacheEntry previous = entry._previous;
CacheEntry next = entry._next;
if (previous != null) {
previous._next = next;
}
if (next != null) {
next._previous = previous;
}
entry._previous = null;
entry._next = null;
}
}
/**
* Purge least recently used object from the cache
*
* @return recyclable CacheEntry
*/
protected CacheEntry purgeEntry() {
synchronized (_hash){
CacheEntry entry = _first;
if (entry == null)
return new CacheEntry(-1, null);
removeEntry(entry);
_hash.remove(entry._recid);
entry._obj = null;
return entry;
}
}
@SuppressWarnings("unchecked")
static final class CacheEntry {
protected long _recid;
protected Object _obj;
protected CacheEntry _previous;
protected CacheEntry _next;
CacheEntry(long recid, Object obj) {
_recid = recid;
_obj = obj;
}
}
public void clearCache() {
if(debug)
System.err.println("DBCache: Clear cache");
// discard all cache entries since we don't know which entries
// where part of the transaction
synchronized (_hash){
_hash.clear();
_first = null;
_last = null;
//clear dirties
updateCacheEntries();
}
}
}

View File

@ -1,401 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A DB wrapping and caching another DB.
*
* @author Jan Kotek
* @author Alex Boisvert
* @author Cees de Groot
*
* TODO add 'cache miss' statistics
*/
public class DBCacheRef
extends DBCache {
private static final boolean debug = false;
/**
* If Soft Cache is enabled, this contains softly referenced clean entries.
* If entry became dirty, it is moved to _hash with limited size.
* This map is accessed from SoftCache Disposer thread, so all access must be
* synchronized
*/
protected LongHashMap _softHash;
/**
* Reference queue used to collect Soft Cache entries
*/
protected ReferenceQueue<ReferenceCacheEntry> _refQueue;
/**
* Thread in which Soft Cache references are disposed
*/
protected Thread _softRefThread;
protected static AtomicInteger threadCounter = new AtomicInteger(0);
/** counter which counts number of insert since last 'action'*/
protected int insertCounter = 0;
private final boolean _autoClearReferenceCacheOnLowMem;
private final byte _cacheType;
/**
* Construct a CacheRecordManager wrapping another DB and
* using a given cache policy.
*/
public DBCacheRef(String filename, boolean readonly, boolean transactionDisabled,
Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
boolean deleteFilesAfterClose,
byte cacheType, boolean cacheAutoClearOnLowMem, boolean lockingDisabled) {
super(filename, readonly, transactionDisabled,
cipherIn, cipherOut, useRandomAccessFile,
deleteFilesAfterClose, lockingDisabled);
this._cacheType = cacheType;
_autoClearReferenceCacheOnLowMem = cacheAutoClearOnLowMem;
_softHash = new LongHashMap<ReferenceCacheEntry>();
_refQueue = new ReferenceQueue<ReferenceCacheEntry>();
_softRefThread = new Thread(
new SoftRunnable(this, _refQueue),
"JDBM Soft Cache Disposer " + (threadCounter.incrementAndGet()));
_softRefThread.setDaemon(true);
_softRefThread.start();
}
void clearCacheIfLowOnMem() {
insertCounter = 0;
if(!_autoClearReferenceCacheOnLowMem)
return;
Runtime r = Runtime.getRuntime();
long max = r.maxMemory();
if(max == Long.MAX_VALUE)
return;
double free = r.freeMemory();
double total = r.totalMemory();
//We believe that free refers to total not max.
//Increasing heap size to max would increase to max
free = free + (max-total);
if(debug)
System.err.println("DBCache: freemem = " +free + " = "+(free/max)+"%");
if(free<1e7 || free*4 <max)
clearCache();
}
public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
if (disableCache)
return super.fetch(recid, serializer, disableCache);
else
return fetch(recid, serializer);
}
public synchronized void delete(long recid)
throws IOException {
checkNotClosed();
super.delete(recid);
synchronized (_hashDirties){
_hashDirties.remove(recid);
}
synchronized (_softHash) {
Object e = _softHash.remove(recid);
if (e != null && e instanceof ReferenceCacheEntry) {
((ReferenceCacheEntry)e).clear();
}
}
if(needsAutoCommit())
commit();
}
public synchronized <A> void update(final long recid, A obj, Serializer<A> serializer) throws IOException {
checkNotClosed();
synchronized (_softHash) {
//soft cache can not contain dirty objects
Object e = _softHash.remove(recid);
if (e != null && e instanceof ReferenceCacheEntry) {
((ReferenceCacheEntry)e).clear();
}
}
synchronized (_hashDirties){
//put into dirty cache
final DirtyCacheEntry e = new DirtyCacheEntry();
e._recid = recid;
e._obj = obj;
e._serializer = serializer;
_hashDirties.put(recid,e);
}
if(needsAutoCommit())
commit();
}
public synchronized <A> A fetch(long recid, Serializer<A> serializer)
throws IOException {
checkNotClosed();
synchronized (_softHash) {
Object e = _softHash.get(recid);
if (e != null) {
if(e instanceof ReferenceCacheEntry)
e = ((ReferenceCacheEntry)e).get();
if (e != null) {
return (A) e;
}
}
}
synchronized (_hashDirties){
DirtyCacheEntry e2 = _hashDirties.get(recid);
if(e2!=null){
return (A) e2._obj;
}
}
A value = super.fetch(recid, serializer);
if(needsAutoCommit())
commit();
synchronized (_softHash) {
if (_cacheType == SOFT)
_softHash.put(recid, new SoftCacheEntry(recid, value, _refQueue));
else if (_cacheType == WEAK)
_softHash.put(recid, new WeakCacheEntry(recid, value, _refQueue));
else
_softHash.put(recid,value);
}
return value;
}
public synchronized void close() {
checkNotClosed();
updateCacheEntries();
super.close();
_softHash = null;
_softRefThread.interrupt();
}
public synchronized void rollback() {
checkNotClosed();
// discard all cache entries since we don't know which entries
// where part of the transaction
synchronized (_softHash) {
Iterator<ReferenceCacheEntry> iter = _softHash.valuesIterator();
while (iter.hasNext()) {
ReferenceCacheEntry e = iter.next();
e.clear();
}
_softHash.clear();
}
super.rollback();
}
protected boolean isCacheEntryDirty(DirtyCacheEntry entry) {
return _hashDirties.get(entry._recid) != null;
}
protected void setCacheEntryDirty(DirtyCacheEntry entry, boolean dirty) {
if (dirty) {
_hashDirties.put(entry._recid, entry);
} else {
_hashDirties.remove(entry._recid);
}
}
interface ReferenceCacheEntry {
long getRecid();
void clear();
Object get();
}
@SuppressWarnings("unchecked")
static final class SoftCacheEntry extends SoftReference implements ReferenceCacheEntry {
protected final long _recid;
public long getRecid() {
return _recid;
}
SoftCacheEntry(long recid, Object obj, ReferenceQueue queue) {
super(obj, queue);
_recid = recid;
}
}
@SuppressWarnings("unchecked")
static final class WeakCacheEntry extends WeakReference implements ReferenceCacheEntry {
protected final long _recid;
public long getRecid() {
return _recid;
}
WeakCacheEntry(long recid, Object obj, ReferenceQueue queue) {
super(obj, queue);
_recid = recid;
}
}
/**
* Runs in separate thread and cleans SoftCache.
* Runnable auto exists when CacheRecordManager is GCed
*
* @author Jan Kotek
*/
static final class SoftRunnable implements Runnable {
private ReferenceQueue<ReferenceCacheEntry> entryQueue;
private WeakReference<DBCacheRef> db2;
public SoftRunnable(DBCacheRef db,
ReferenceQueue<ReferenceCacheEntry> entryQueue) {
this.db2 = new WeakReference<DBCacheRef>(db);
this.entryQueue = entryQueue;
}
public void run() {
while (true) try {
//collect next item from cache,
//limit 10000 ms is to keep periodically checking if db was GCed
ReferenceCacheEntry e = (ReferenceCacheEntry) entryQueue.remove(10000);
//check if db was GCed, cancel in that case
DBCacheRef db = db2.get();
if (db == null)
return;
if (e != null) {
synchronized (db._softHash) {
int counter = 0;
while (e != null) {
db._softHash.remove(e.getRecid());
e = (SoftCacheEntry) entryQueue.poll();
if(debug)
counter++;
}
if(debug)
System.err.println("DBCache: "+counter+" objects released from ref cache.");
}
}else{
//check memory consumption every 10 seconds
db.clearCacheIfLowOnMem();
}
} catch (InterruptedException e) {
return;
} catch (Throwable e) {
//this thread must keep spinning,
//otherwise SoftCacheEntries would not be disposed
e.printStackTrace();
}
}
}
public void clearCache() {
if(debug)
System.err.println("DBCache: Clear cache");
synchronized (_softHash) {
if(_cacheType!=HARD){
Iterator<ReferenceCacheEntry> iter = _softHash.valuesIterator();
while (iter.hasNext()) {
ReferenceCacheEntry e = iter.next();
e.clear();
}
}
_softHash.clear();
}
}
}

View File

@ -1,351 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOError;
import java.security.spec.KeySpec;
/**
* Class used to configure and create DB. It uses builder pattern.
*/
public class DBMaker {
private byte cacheType = DBCacheRef.MRU;
private int mruCacheSize = 2048;
private String location = null;
private boolean disableTransactions = false;
private boolean lockingDisabled = false;
private boolean readonly = false;
private String password = null;
private boolean useAES256Bit = true;
private boolean useRandomAccessFile = false;
private boolean autoClearRefCacheOnLowMem = true;
private boolean closeOnJVMExit = false;
private boolean deleteFilesAfterCloseFlag = false;
private DBMaker(){}
/**
* Creates new DBMaker and sets file to load data from.
* @param file to load data from
* @return new DBMaker
*/
public static DBMaker openFile(String file){
DBMaker m = new DBMaker();
m.location = file;
return m;
}
/**
* Creates new DBMaker which uses in memory store. Data will be lost after JVM exits.
* @return new DBMaker
*/
public static DBMaker openMemory(){
return new DBMaker();
}
/**
* Open store in zip file
*
* @param zip file
* @return new DBMaker
*/
public static DBMaker openZip(String zip) {
DBMaker m = new DBMaker();
m.location = "$$ZIP$$://"+zip;
return m;
}
static String isZipFileLocation(String location){
String match = "$$ZIP$$://";
if( location.startsWith(match)){
return location.substring(match.length());
}
return null;
}
/**
* Use WeakReference for cache.
* This cache does not improve performance much,
* but prevents JDBM from creating multiple instances of the same object.
*
* @return this builder
*/
public DBMaker enableWeakCache() {
cacheType = DBCacheRef.WEAK;
return this;
}
/**
* Use SoftReference for cache.
* This cache greatly improves performance if you have enoguth memory.
* Instances in cache are Garbage Collected when memory gets low
*
* @return this builder
*/
public DBMaker enableSoftCache() {
cacheType = DBCacheRef.SOFT;
return this;
}
/**
* Use hard reference for cache.
* This greatly improves performance if there is enought memory
* Hard cache has smaller memory overhead then Soft or Weak, because
* reference objects and queue does not have to be maintained
*
* @return this builder
*/
public DBMaker enableHardCache() {
cacheType = DBCacheRef.SOFT;
return this;
}
/**
* Use 'Most Recently Used' cache with limited size.
* Oldest instances are released from cache when new instances are fetched.
* This cache is not cleared by GC. Is good for systems with limited memory.
* <p/>
* Default size for MRU cache is 2048 records.
*
* @return this builder
*/
public DBMaker enableMRUCache() {
cacheType = DBCacheRef.MRU;
return this;
}
/**
*
* Sets 'Most Recently Used' cache size. This cache is activated by default with size 2048
*
* @param cacheSize number of instances which will be kept in cache.
* @return this builder
*/
public DBMaker setMRUCacheSize(int cacheSize) {
if (cacheSize < 0) throw new IllegalArgumentException("Cache size is smaller than zero");
cacheType = DBCacheRef.MRU;
mruCacheSize = cacheSize;
return this;
}
/**
* If reference (soft,weak or hard) cache is enabled,
* GC may not release references fast enough (or not at all in case of hard cache).
* So JDBM periodically checks amount of free heap memory.
* If free memory is less than 25% or 10MB,
* JDBM completely clears its reference cache to prevent possible memory issues.
* <p>
* Calling this method disables auto cache clearing when mem is low.
* And of course it can cause some out of memory exceptions.
*
* @return this builder
*/
public DBMaker disableCacheAutoClear(){
this.autoClearRefCacheOnLowMem = false;
return this;
}
/**
* Enabled storage encryption using AES cipher. JDBM supports both 128 bit and 256 bit encryption if JRE provides it.
* There are some restrictions on AES 256 bit and not all JREs have it by default.
* <p/>
* Storage can not be read (decrypted), unless the key is provided next time it is opened
*
* @param password used to encrypt store
* @param useAES256Bit if true strong AES 256 bit encryption is used. Otherwise more usual AES 128 bit is used.
* @return this builder
*/
public DBMaker enableEncryption(String password, boolean useAES256Bit) {
this.password = password;
this.useAES256Bit = useAES256Bit;
return this;
}
/**
* Make DB readonly.
* Update/delete/insert operation will throw 'UnsupportedOperationException'
*
* @return this builder
*/
public DBMaker readonly() {
readonly = true;
return this;
}
/**
* Disable cache completely
*
* @return this builder
*/
public DBMaker disableCache() {
cacheType = DBCacheRef.NONE;
return this;
}
/**
* Option to disable transaction (to increase performance at the cost of potential data loss).
* Transactions are enabled by default
* <p/>
* Switches off transactioning for the record manager. This means
* that a) a transaction log is not kept, and b) writes aren't
* synch'ed after every update. Writes are cached in memory and then flushed
* to disk every N writes. You may also flush writes manually by calling commit().
* This is useful when batch inserting into a new database.
* <p/>
* When using this, database must be properly closed before JVM shutdown.
* Failing to do so may and WILL corrupt store.
*
* @return this builder
*/
public DBMaker disableTransactions() {
this.disableTransactions = true;
return this;
}
/**
* Disable file system based locking (for file systems that do not support it).
*
* Locking is not supported by many remote or distributed file systems; such
* as Lustre and NFS. Attempts to perform locks will result in an
* IOException with the message "Function not implemented".
*
* Disabling locking will avoid this issue, though of course it comes with
* all the issues of uncontrolled file access.
*
* @return this builder
*/
public DBMaker disableLocking(){
this.lockingDisabled = true;
return this;
}
/**
* By default JDBM uses mapped memory buffers to read from files.
* But this may behave strangely on some platforms.
* Safe alternative is to use old RandomAccessFile rather then mapped ByteBuffer.
* There is typically slower (pages needs to be copyed into memory on every write).
*
* @return this builder
*/
public DBMaker useRandomAccessFile(){
this.useRandomAccessFile = true;
return this;
}
/**
* Registers shutdown hook and close database on JVM exit, if it was not already closed;
*
* @return this builder
*/
public DBMaker closeOnExit(){
this.closeOnJVMExit = true;
return this;
}
/**
* Delete all storage files after DB is closed
*
* @return this builder
*/
public DBMaker deleteFilesAfterClose(){
this.deleteFilesAfterCloseFlag = true;
return this;
}
/**
* Opens database with settings earlier specified in this builder.
*
* @return new DB
* @throws java.io.IOError if db could not be opened
*/
public DB make() {
Cipher cipherIn = null;
Cipher cipherOut = null;
if (password != null) try {
//initialize ciphers
//this code comes from stack owerflow
//http://stackoverflow.com/questions/992019/java-256bit-aes-encryption/992413#992413
byte[] salt = new byte[]{3, -34, 123, 53, 78, 121, -12, -1, 45, -12, -48, 89, 11, 100, 99, 8};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1024, useAES256Bit?256:128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
String transform = "AES/CBC/NoPadding";
IvParameterSpec params = new IvParameterSpec(salt);
cipherIn = Cipher.getInstance(transform);
cipherIn.init(Cipher.ENCRYPT_MODE, secret, params);
cipherOut = Cipher.getInstance(transform);
cipherOut.init(Cipher.DECRYPT_MODE, secret, params);
//sanity check, try with page size
byte[] data = new byte[Storage.PAGE_SIZE];
byte[] encData = cipherIn.doFinal(data);
if (encData.length != Storage.PAGE_SIZE)
throw new Error("Page size changed after encryption, make sure you use '/NoPadding'");
byte[] data2 = cipherOut.doFinal(encData);
for (int i = 0; i < data.length; i++) {
if (data[i] != data2[i]) throw new Error("Encryption provided by JRE does not work");
}
} catch (Exception e) {
throw new IOError(e);
}
DBAbstract db = null;
if (cacheType == DBCacheRef.MRU){
db = new DBCacheMRU(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag, mruCacheSize,lockingDisabled);
}else if( cacheType == DBCacheRef.SOFT || cacheType == DBCacheRef.HARD || cacheType == DBCacheRef.WEAK) {
db = new DBCacheRef(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag, cacheType,autoClearRefCacheOnLowMem,lockingDisabled);
} else if (cacheType == DBCacheRef.NONE) {
db = new DBStore(location, readonly, disableTransactions, cipherIn, cipherOut,useRandomAccessFile,deleteFilesAfterCloseFlag,lockingDisabled);
} else {
throw new IllegalArgumentException("Unknown cache type: " + cacheType);
}
if(closeOnJVMExit){
db.addShutdownHook();
}
return db;
}
}

View File

@ -1,928 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* This class manages records, which are uninterpreted blobs of data. The
* set of operations is simple and straightforward: you communicate with
* the class using long "rowids" and byte[] data blocks. Rowids are returned
* on inserts and you can stash them away someplace safe to be able to get
* back to them. Data blocks can be as long as you wish, and may have
* lengths different from the original when updating.
* <p/>
* Operations are synchronized, so that only one of them will happen
* concurrently even if you hammer away from multiple threads. Operations
* are made atomic by keeping a transaction log which is recovered after
* a crash, so the operations specified by this interface all have ACID
* properties.
* <p/>
* You identify a file by just the name. The package attaches <tt>.db</tt>
* for the database file, and <tt>.lg</tt> for the transaction log. The
* transaction log is synchronized regularly and then restarted, so don't
* worry if you see the size going up and down.
*
* @author Alex Boisvert
* @author Cees de Groot
*/
class DBStore
extends DBAbstract {
/**
* Version of storage. It should be safe to open lower versions, but engine should throw exception
* while opening new versions (as it contains unsupported features or serialization)
*/
static final long STORE_FORMAT_VERSION = 1L;
/**
* Underlying file for store records.
*/
private PageFile _file;
/**
* Page manager for physical manager.
*/
private PageManager _pageman;
/**
* Physical row identifier manager.
*/
private PhysicalRowIdManager _physMgr;
/**
* Indicated that store is opened for readonly operations
* If true, store will throw UnsupportedOperationException when update/insert/delete operation is called
*/
private final boolean readonly;
final boolean transactionsDisabled;
private final boolean deleteFilesAfterClose;
private static final int AUTOCOMMIT_AFTER_N_PAGES = 1024 * 5;
boolean commitInProgress = false;
/**
* cipher used for decryption, may be null
*/
private Cipher cipherOut;
/**
* cipher used for encryption, may be null
*/
private Cipher cipherIn;
private boolean useRandomAccessFile;
private boolean lockingDisabled;
void checkCanWrite() {
if (readonly)
throw new UnsupportedOperationException("Could not write, store is opened as read-only");
}
/**
* Logigal to Physical row identifier manager.
*/
private LogicalRowIdManager _logicMgr;
/**
* Static debugging flag
*/
public static final boolean DEBUG = false;
static final long PREALOCATE_PHYS_RECID = Short.MIN_VALUE;
static final Object PREALOCATE_OBJ = new Object();
private final DataInputOutput buffer = new DataInputOutput();
private boolean bufferInUse = false;
private final String _filename;
public DBStore(String filename, boolean readonly, boolean transactionDisabled, boolean lockingDisabled) throws IOException {
this(filename, readonly, transactionDisabled, null, null, false,false,false);
}
/**
* Creates a record manager for the indicated file
*
* @throws IOException when the file cannot be opened or is not
* a valid file content-wise.
*/
public DBStore(String filename, boolean readonly, boolean transactionDisabled,
Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
boolean deleteFilesAfterClose, boolean lockingDisabled){
_filename = filename;
this.readonly = readonly;
this.transactionsDisabled = transactionDisabled;
this.cipherIn = cipherIn;
this.cipherOut = cipherOut;
this.useRandomAccessFile = useRandomAccessFile;
this.deleteFilesAfterClose = deleteFilesAfterClose;
this.lockingDisabled = lockingDisabled;
reopen();
}
private void reopen() {
try{
_file = new PageFile(_filename, readonly, transactionsDisabled, cipherIn, cipherOut,useRandomAccessFile,lockingDisabled);
_pageman = new PageManager(_file);
_physMgr = new PhysicalRowIdManager(_file, _pageman);
_logicMgr = new LogicalRowIdManager(_file, _pageman);
long versionNumber = getRoot(STORE_VERSION_NUMBER_ROOT);
if (versionNumber > STORE_FORMAT_VERSION)
throw new IOException("Unsupported version of store. Please update JDBM. Minimal supported ver:" + STORE_FORMAT_VERSION + ", store ver:" + versionNumber);
if (!readonly)
setRoot(STORE_VERSION_NUMBER_ROOT, STORE_FORMAT_VERSION);
}catch(IOException e){
throw new IOError(e);
}
}
/**
* Closes the record manager.
*
* @throws IOException when one of the underlying I/O operations fails.
*/
public synchronized void close() {
checkNotClosed();
try {
super.close();
_pageman.close();
_file.close();
if(deleteFilesAfterClose)
_file.storage.deleteAllFiles();
_pageman = null;
_file = null;
} catch (IOException e) {
throw new IOError(e);
}
}
public boolean isClosed() {
return _pageman==null;
}
public synchronized <A> long insert(final A obj, final Serializer<A> serializer, final boolean disableCache)
throws IOException {
checkNotClosed();
checkCanWrite();
if (needsAutoCommit()) {
commit();
}
if (bufferInUse) {
//current reusable buffer is in use, have to fallback into creating new instances
DataInputOutput buffer2 = new DataInputOutput();
return insert2(obj, serializer, buffer2);
}
try {
bufferInUse = true;
return insert2(obj, serializer, buffer);
} finally {
bufferInUse = false;
}
}
boolean needsAutoCommit() {
return transactionsDisabled && !commitInProgress &&
(_file.getDirtyPageCount() >= AUTOCOMMIT_AFTER_N_PAGES );
}
private <A> long insert2(A obj, Serializer<A> serializer, DataInputOutput buf)
throws IOException {
buf.reset();
long physRowId;
if(obj==PREALOCATE_OBJ){
//if inserted record is PREALOCATE_OBJ , it gets special handling.
//it is inserted only into _logicMgr with special value to indicate null
//this is used to preallocate recid for lazy inserts in cache
physRowId = PREALOCATE_PHYS_RECID;
}else{
serializer.serialize(buf, obj);
if(buf.getPos()>RecordHeader.MAX_RECORD_SIZE){
throw new IllegalArgumentException("Too big record. JDBM only supports record size up to: "+RecordHeader.MAX_RECORD_SIZE+" bytes. Record size was: "+buf.getPos());
}
physRowId = _physMgr.insert(buf.getBuf(), 0, buf.getPos());
}
final long recid = _logicMgr.insert(physRowId);
if (DEBUG) {
System.out.println("BaseRecordManager.insert() recid " + recid + " length " + buf.getPos());
}
return compressRecid(recid);
}
public synchronized void delete(long logRowId)
throws IOException {
checkNotClosed();
checkCanWrite();
if (logRowId <= 0) {
throw new IllegalArgumentException("Argument 'recid' is invalid: "
+ logRowId);
}
if (needsAutoCommit()) {
commit();
}
if (DEBUG) {
System.out.println("BaseRecordManager.delete() recid " + logRowId);
}
logRowId = decompressRecid(logRowId);
long physRowId = _logicMgr.fetch(logRowId);
_logicMgr.delete(logRowId);
if(physRowId!=PREALOCATE_PHYS_RECID){
_physMgr.free(physRowId);
}
}
public synchronized <A> void update(long recid, A obj, Serializer<A> serializer)
throws IOException {
checkNotClosed();
checkCanWrite();
if (recid <= 0) {
throw new IllegalArgumentException("Argument 'recid' is invalid: "
+ recid);
}
if (needsAutoCommit()) {
commit();
}
if (bufferInUse) {
//current reusable buffer is in use, have to create new instances
DataInputOutput buffer2 = new DataInputOutput();
update2(recid, obj, serializer, buffer2);
return;
}
try {
bufferInUse = true;
update2(recid, obj, serializer, buffer);
} finally {
bufferInUse = false;
}
}
private <A> void update2(long logRecid, final A obj, final Serializer<A> serializer, final DataInputOutput buf)
throws IOException {
logRecid = decompressRecid(logRecid);
long physRecid = _logicMgr.fetch(logRecid);
if (physRecid == 0)
throw new IOException("Can not update, recid does not exist: " + logRecid);
buf.reset();
serializer.serialize(buf, obj);
if (DEBUG) {
System.out.println("BaseRecordManager.update() recid " + logRecid + " length " + buf.getPos());
}
long newRecid =
physRecid!=PREALOCATE_PHYS_RECID?
_physMgr.update(physRecid, buf.getBuf(), 0, buf.getPos()):
//previous record was only virtual and does not actually exist, so make new insert
_physMgr.insert(buf.getBuf(),0,buf.getPos());
_logicMgr.update(logRecid, newRecid);
}
public synchronized <A> A fetch(final long recid, final Serializer<A> serializer)
throws IOException {
checkNotClosed();
if (recid <= 0) {
throw new IllegalArgumentException("Argument 'recid' is invalid: " + recid);
}
if (bufferInUse) {
//current reusable buffer is in use, have to create new instances
DataInputOutput buffer2 = new DataInputOutput();
return fetch2(recid, serializer, buffer2);
}
try {
bufferInUse = true;
return fetch2(recid, serializer, buffer);
} finally {
bufferInUse = false;
}
}
public synchronized <A> A fetch(long recid, Serializer<A> serializer, boolean disableCache) throws IOException {
//we dont have any cache, so can ignore disableCache parameter
return fetch(recid, serializer);
}
private <A> A fetch2(long recid, final Serializer<A> serializer, final DataInputOutput buf)
throws IOException {
recid = decompressRecid(recid);
buf.reset();
long physLocation = _logicMgr.fetch(recid);
if (physLocation == 0) {
//throw new IOException("Record not found, recid: "+recid);
return null;
}
if(physLocation == PREALOCATE_PHYS_RECID){
throw new InternalError("cache should prevent this!");
}
_physMgr.fetch(buf, physLocation);
if (DEBUG) {
System.out.println("BaseRecordManager.fetch() recid " + recid + " length " + buf.getPos());
}
buf.resetForReading();
try {
return serializer.deserialize(buf); //TODO there should be write limit to throw EOFException
} catch (ClassNotFoundException e) {
throw new IOError(e);
}
}
byte[] fetchRaw(long recid) throws IOException {
recid = decompressRecid(recid);
long physLocation = _logicMgr.fetch(recid);
if (physLocation == 0) {
//throw new IOException("Record not found, recid: "+recid);
return null;
}
DataInputOutput i = new DataInputOutput();
_physMgr.fetch(i, physLocation);
return i.toByteArray();
}
public synchronized long getRoot(final byte id){
checkNotClosed();
return _pageman.getFileHeader().fileHeaderGetRoot(id);
}
public synchronized void setRoot(final byte id, final long rowid){
checkNotClosed();
checkCanWrite();
_pageman.getFileHeader().fileHeaderSetRoot(id, rowid);
}
public synchronized void commit() {
try {
commitInProgress = true;
checkNotClosed();
checkCanWrite();
/** flush free phys rows into pages*/
_physMgr.commit();
_logicMgr.commit();
/**commit pages */
_pageman.commit();
} catch (IOException e) {
throw new IOError(e);
}finally {
commitInProgress= false;
}
}
public synchronized void rollback() {
if (transactionsDisabled)
throw new IllegalAccessError("Transactions are disabled, can not rollback");
try {
checkNotClosed();
_physMgr.rollback();
_logicMgr.rollback();
_pageman.rollback();
super.rollback();
} catch (IOException e) {
throw new IOError(e);
}
}
public void copyToZip(String zipFile) {
try {
String zip = zipFile;
String zip2 = "db";
ZipOutputStream z = new ZipOutputStream(new FileOutputStream(zip));
//copy zero pages
{
String file = zip2 + 0;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, _pageman.getHeaderBufData()));
z.closeEntry();
}
//iterate over pages and create new file for each
for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo page = _file.get(pageid);
String file = zip2 + pageid;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, page.getData()));
z.closeEntry();
_file.release(page);
}
for (long pageid = _pageman.getFirst(Magic.FREELOGIDS_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo page = _file.get(pageid);
String file = zip2 + pageid;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, page.getData()));
z.closeEntry();
_file.release(page);
}
for (long pageid = _pageman.getFirst(Magic.USED_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo page = _file.get(pageid);
String file = zip2 + pageid;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, page.getData()));
z.closeEntry();
_file.release(page);
}
for (long pageid = _pageman.getFirst(Magic.FREEPHYSIDS_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo page = _file.get(pageid);
String file = zip2 + pageid;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, page.getData()));
z.closeEntry();
_file.release(page);
}
for (long pageid = _pageman.getFirst(Magic.FREEPHYSIDS_ROOT_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo page = _file.get(pageid);
String file = zip2 + pageid;
z.putNextEntry(new ZipEntry(file));
z.write(Utils.encrypt(cipherIn, page.getData()));
z.closeEntry();
_file.release(page);
}
z.close();
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized void clearCache() {
//no cache
}
private long statisticsCountPages(short pageType) throws IOException {
long pageCounter = 0;
for (long pageid = _pageman.getFirst(pageType);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
pageCounter++;
}
return pageCounter;
}
public synchronized String calculateStatistics() {
checkNotClosed();
try {
final StringBuilder b = new StringBuilder();
//count pages
{
b.append("PAGES:\n");
long total = 0;
long pages = statisticsCountPages(Magic.USED_PAGE);
total += pages;
b.append(" " + pages + " used pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
pages = statisticsCountPages(Magic.TRANSLATION_PAGE);
total += pages;
b.append(" " + pages + " record translation pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
pages = statisticsCountPages(Magic.FREE_PAGE);
total += pages;
b.append(" " + pages + " free (unused) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
pages = statisticsCountPages(Magic.FREEPHYSIDS_PAGE);
total += pages;
b.append(" " + pages + " free (phys) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
pages = statisticsCountPages(Magic.FREELOGIDS_PAGE);
total += pages;
b.append(" " + pages + " free (logical) pages with size " + Utils.formatSpaceUsage(pages * Storage.PAGE_SIZE) + "\n");
b.append(" Total number of pages is " + total + " with size " + Utils.formatSpaceUsage(total * Storage.PAGE_SIZE) + "\n");
}
{
b.append("RECORDS:\n");
long recordCount = 0;
long freeRecordCount = 0;
long maximalRecordSize = 0;
long maximalAvailSizeDiff = 0;
long totalRecordSize = 0;
long totalAvailDiff = 0;
//count records
for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo io = _file.get(pageid);
for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
final int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
final long physLoc = io.pageHeaderGetLocation((short) pos);
if (physLoc == 0) {
freeRecordCount++;
continue;
}
if(physLoc == PREALOCATE_PHYS_RECID){
continue;
}
recordCount++;
//get size
PageIo page = _file.get(physLoc>>> Storage.PAGE_SIZE_SHIFT);
final short physOffset =(short) (physLoc & Storage.OFFSET_MASK);
int availSize = RecordHeader.getAvailableSize(page, physOffset);
int currentSize = RecordHeader.getCurrentSize(page, physOffset);
_file.release(page);
maximalAvailSizeDiff = Math.max(maximalAvailSizeDiff, availSize - currentSize);
maximalRecordSize = Math.max(maximalRecordSize, currentSize);
totalAvailDiff += availSize - currentSize;
totalRecordSize += currentSize;
}
_file.release(io);
}
b.append(" Contains " + recordCount + " records and " + freeRecordCount + " free slots.\n");
b.append(" Total space occupied by data is " + Utils.formatSpaceUsage(totalRecordSize) + "\n");
b.append(" Average data size in record is " + Utils.formatSpaceUsage(Math.round(1D * totalRecordSize / recordCount)) + "\n");
b.append(" Maximal data size in record is " + Utils.formatSpaceUsage(maximalRecordSize) + "\n");
b.append(" Space wasted in record fragmentation is " + Utils.formatSpaceUsage(totalAvailDiff) + "\n");
b.append(" Maximal space wasted in single record fragmentation is " + Utils.formatSpaceUsage(maximalAvailSizeDiff) + "\n");
}
return b.toString();
} catch (IOException e) {
throw new IOError(e);
}
}
public synchronized void defrag(boolean sortCollections) {
try {
checkNotClosed();
checkCanWrite();
commit();
final String filename2 = _filename + "_defrag" + System.currentTimeMillis();
final String filename1 = _filename;
DBStore db2 = new DBStore(filename2, false, true, cipherIn, cipherOut, false,false,false);
//recreate logical file with original page layout
{
//find minimal logical pageid (logical pageids are negative)
LongHashMap<String> logicalPages = new LongHashMap<String>();
long minpageid = 0;
for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
minpageid = Math.min(minpageid, pageid);
logicalPages.put(pageid, Utils.EMPTY_STRING);
}
//fill second db with logical pages
long pageCounter = 0;
for (
long pageid = db2._pageman.allocate(Magic.TRANSLATION_PAGE);
pageid >= minpageid;
pageid = db2._pageman.allocate(Magic.TRANSLATION_PAGE)
) {
pageCounter++;
if (pageCounter % 1000 == 0)
db2.commit();
}
logicalPages = null;
}
//reinsert collections so physical records are located near each other
//iterate over named object recids, it is sorted with TreeSet
if(sortCollections){
long nameRecid = getRoot(NAME_DIRECTORY_ROOT);
Collection<Long> recids = new TreeSet<Long>();
if(nameRecid!=0){
HTree<String,Long> m = fetch(nameRecid);
recids.addAll(m.values());
}
for (Long namedRecid : recids) {
Object obj = fetch(namedRecid);
if (obj instanceof LinkedList) {
LinkedList2.defrag(namedRecid, this, db2);
} else if (obj instanceof HTree) {
HTree.defrag(namedRecid, this, db2);
} else if (obj instanceof BTree) {
BTree.defrag(namedRecid, this, db2);
}
}
}
for (long pageid = _pageman.getFirst(Magic.TRANSLATION_PAGE);
pageid != 0;
pageid = _pageman.getNext(pageid)
) {
PageIo io = _file.get(pageid);
for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
final int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
if (pos > Short.MAX_VALUE)
throw new Error();
//write to new file
final long logicalRowId = ((-pageid) << Storage.PAGE_SIZE_SHIFT) + (long) pos;
//read from logical location in second db,
//check if record was already inserted as part of collections
if (db2._pageman.getLast(Magic.TRANSLATION_PAGE) <= pageid &&
db2._logicMgr.fetch(logicalRowId) != 0) {
//yes, this record already exists in second db
continue;
}
//get physical location in this db
final long physRowId = io.pageHeaderGetLocation((short) pos);
if (physRowId == 0)
continue;
if (physRowId == PREALOCATE_PHYS_RECID){
db2._logicMgr.forceInsert(logicalRowId, physRowId);
continue;
}
//read from physical location at this db
DataInputOutput b = new DataInputOutput();
_physMgr.fetch(b, physRowId);
byte[] bb = b.toByteArray();
//force insert into other file, without decompressing logical id to external form
long physLoc = db2._physMgr.insert(bb, 0, bb.length);
db2._logicMgr.forceInsert(logicalRowId, physLoc);
}
_file.release(io);
db2.commit();
}
for(byte b = 0;b<Magic.FILE_HEADER_NROOTS;b++){
db2.setRoot(b, getRoot(b));
}
db2.close();
_pageman.close();
_file.close();
List<File> filesToDelete = new ArrayList<File>();
//now rename old files
String[] exts = {StorageDiskMapped.IDR, StorageDiskMapped.DBR};
for (String ext : exts) {
String f1 = filename1 + ext;
String f2 = filename2 + "_OLD" + ext;
//first rename transaction log
File f1t = new File(f1 + StorageDisk.transaction_log_file_extension);
File f2t = new File(f2 + StorageDisk.transaction_log_file_extension);
f1t.renameTo(f2t);
filesToDelete.add(f2t);
//rename data files, iterate until file exist
for (int i = 0; ; i++) {
File f1d = new File(f1 + "." + i);
if (!f1d.exists()) break;
File f2d = new File(f2 + "." + i);
f1d.renameTo(f2d);
filesToDelete.add(f2d);
}
}
//rename new files
for (String ext : exts) {
String f1 = filename2 + ext;
String f2 = filename1 + ext;
//first rename transaction log
File f1t = new File(f1 + StorageDisk.transaction_log_file_extension);
File f2t = new File(f2 + StorageDisk.transaction_log_file_extension);
f1t.renameTo(f2t);
//rename data files, iterate until file exist
for (int i = 0; ; i++) {
File f1d = new File(f1 + "." + i);
if (!f1d.exists()) break;
File f2d = new File(f2 + "." + i);
f1d.renameTo(f2d);
}
}
for (File d : filesToDelete) {
d.delete();
}
reopen();
} catch (IOException e) {
throw new IOError(e);
}
}
/**
* Insert data at forced logicalRowId, use only for defragmentation !!
*
* @param logicalRowId
* @param data
* @throws IOException
*/
void forceInsert(long logicalRowId, byte[] data) throws IOException {
logicalRowId = decompressRecid(logicalRowId);
if (needsAutoCommit()) {
commit();
}
long physLoc = _physMgr.insert(data, 0, data.length);
_logicMgr.forceInsert(logicalRowId, physLoc);
}
/**
* Returns number of records stored in database.
* Is used for unit tests
*/
long countRecords() throws IOException {
long counter = 0;
long page = _pageman.getFirst(Magic.TRANSLATION_PAGE);
while (page != 0) {
PageIo io = _file.get(page);
for (int i = 0; i < _logicMgr.ELEMS_PER_PAGE; i += 1) {
int pos = Magic.PAGE_HEADER_SIZE + i * Magic.PhysicalRowId_SIZE;
if (pos > Short.MAX_VALUE)
throw new Error();
//get physical location
long physRowId = io.pageHeaderGetLocation((short) pos);
if (physRowId != 0)
counter += 1;
}
_file.release(io);
page = _pageman.getNext(page);
}
return counter;
}
private static int COMPRESS_RECID_PAGE_SHIFT = Integer.MIN_VALUE;
static{
int shift = 1;
while((1<<shift) <LogicalRowIdManager.ELEMS_PER_PAGE )
shift++;
COMPRESS_RECID_PAGE_SHIFT = shift;
}
private final static long COMPRESS_RECID_OFFSET_MASK = 0xFFFFFFFFFFFFFFFFL >>> (64- COMPRESS_RECID_PAGE_SHIFT);
/**
* Compress recid from physical form (block - offset) to (block - slot).
* This way resulting number is smaller and can be easier packed with LongPacker
*/
static long compressRecid(final long recid) {
final long page = recid>>> Storage.PAGE_SIZE_SHIFT;
short offset = (short) (recid & Storage.OFFSET_MASK);
offset = (short) (offset - Magic.PAGE_HEADER_SIZE);
if (offset % Magic.PhysicalRowId_SIZE != 0)
throw new InternalError("recid not dividable "+Magic.PhysicalRowId_SIZE);
long slot = offset / Magic.PhysicalRowId_SIZE;
return (page << COMPRESS_RECID_PAGE_SHIFT) + slot;
}
static long decompressRecid(final long recid) {
final long page = recid >>> COMPRESS_RECID_PAGE_SHIFT;
final short offset = (short) ((recid & COMPRESS_RECID_OFFSET_MASK) * Magic.PhysicalRowId_SIZE + Magic.PAGE_HEADER_SIZE);
return (page << Storage.PAGE_SIZE_SHIFT) + (long) offset;
}
}

View File

@ -1,297 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Utility class which implements DataInput and DataOutput on top of byte[] buffer
* with minimal overhead
*
* @author Jan Kotek
*/
class DataInputOutput implements DataInput, DataOutput, ObjectInput, ObjectOutput {
private int pos = 0;
private int count = 0;
private byte[] buf;
public DataInputOutput() {
buf = new byte[8];
}
public DataInputOutput(byte[] data) {
buf = data;
count = data.length;
}
public byte[] getBuf() {
return buf;
}
public int getPos() {
return pos;
}
public void reset() {
pos = 0;
count = 0;
}
public void resetForReading() {
count = pos;
pos = 0;
}
public void reset(byte[] b) {
pos = 0;
buf = b;
count = b.length;
}
public byte[] toByteArray() {
byte[] d = new byte[pos];
System.arraycopy(buf, 0, d, 0, pos);
return d;
}
public int available() {
return count - pos;
}
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
public void readFully(byte[] b, int off, int len) throws IOException {
System.arraycopy(buf, pos, b, off, len);
pos += len;
}
public int skipBytes(int n) throws IOException {
pos += n;
return n;
}
public boolean readBoolean() throws IOException {
return buf[pos++] == 1;
}
public byte readByte() throws IOException {
return buf[pos++];
}
public int readUnsignedByte() throws IOException {
return buf[pos++] & 0xff;
}
public short readShort() throws IOException {
return (short)
(((short) (buf[pos++] & 0xff) << 8) |
((short) (buf[pos++] & 0xff) << 0));
}
public int readUnsignedShort() throws IOException {
return (((int) (buf[pos++] & 0xff) << 8) |
((int) (buf[pos++] & 0xff) << 0));
}
public char readChar() throws IOException {
return (char) readInt();
}
public int readInt() throws IOException {
return
(((buf[pos++] & 0xff) << 24) |
((buf[pos++] & 0xff) << 16) |
((buf[pos++] & 0xff) << 8) |
((buf[pos++] & 0xff) << 0));
}
public long readLong() throws IOException {
return
(((long) (buf[pos++] & 0xff) << 56) |
((long) (buf[pos++] & 0xff) << 48) |
((long) (buf[pos++] & 0xff) << 40) |
((long) (buf[pos++] & 0xff) << 32) |
((long) (buf[pos++] & 0xff) << 24) |
((long) (buf[pos++] & 0xff) << 16) |
((long) (buf[pos++] & 0xff) << 8) |
((long) (buf[pos++] & 0xff) << 0));
}
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public String readLine() throws IOException {
return readUTF();
}
public String readUTF() throws IOException {
return Serialization.deserializeString(this);
}
/**
* make sure there will be enought space in buffer to write N bytes
*/
private void ensureAvail(int n) {
if (pos + n >= buf.length) {
int newSize = Math.max(pos + n, buf.length * 2);
buf = Arrays.copyOf(buf, newSize);
}
}
public void write(int b) throws IOException {
ensureAvail(1);
buf[pos++] = (byte) b;
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
ensureAvail(len);
System.arraycopy(b, off, buf, pos, len);
pos += len;
}
public void writeBoolean(boolean v) throws IOException {
ensureAvail(1);
buf[pos++] = (byte) (v ? 1 : 0);
}
public void writeByte(int v) throws IOException {
ensureAvail(1);
buf[pos++] = (byte) (v);
}
public void writeShort(int v) throws IOException {
ensureAvail(2);
buf[pos++] = (byte) (0xff & (v >> 8));
buf[pos++] = (byte) (0xff & (v >> 0));
}
public void writeChar(int v) throws IOException {
writeInt(v);
}
public void writeInt(int v) throws IOException {
ensureAvail(4);
buf[pos++] = (byte) (0xff & (v >> 24));
buf[pos++] = (byte) (0xff & (v >> 16));
buf[pos++] = (byte) (0xff & (v >> 8));
buf[pos++] = (byte) (0xff & (v >> 0));
}
public void writeLong(long v) throws IOException {
ensureAvail(8);
buf[pos++] = (byte) (0xff & (v >> 56));
buf[pos++] = (byte) (0xff & (v >> 48));
buf[pos++] = (byte) (0xff & (v >> 40));
buf[pos++] = (byte) (0xff & (v >> 32));
buf[pos++] = (byte) (0xff & (v >> 24));
buf[pos++] = (byte) (0xff & (v >> 16));
buf[pos++] = (byte) (0xff & (v >> 8));
buf[pos++] = (byte) (0xff & (v >> 0));
}
public void writeFloat(float v) throws IOException {
ensureAvail(4);
writeInt(Float.floatToIntBits(v));
}
public void writeDouble(double v) throws IOException {
ensureAvail(8);
writeLong(Double.doubleToLongBits(v));
}
public void writeBytes(String s) throws IOException {
writeUTF(s);
}
public void writeChars(String s) throws IOException {
writeUTF(s);
}
public void writeUTF(String s) throws IOException {
Serialization.serializeString(this, s);
}
/** helper method to write data directly from PageIo*/
public void writeFromByteBuffer(ByteBuffer b, int offset, int length) {
ensureAvail(length);
b.position(offset);
b.get(buf,pos,length);
pos+=length;
}
//temp var used for Externalizable
SerialClassInfo serializer;
//temp var used for Externalizable
Serialization.FastArrayList objectStack;
public Object readObject() throws ClassNotFoundException, IOException {
//is here just to implement ObjectInput
//Fake method which reads data from serializer.
//We could probably implement separate wrapper for this, but I want to safe class space
return serializer.deserialize(this, objectStack);
}
public int read() throws IOException {
//is here just to implement ObjectInput
return readUnsignedByte();
}
public int read(byte[] b) throws IOException {
//is here just to implement ObjectInput
readFully(b);
return b.length;
}
public int read(byte[] b, int off, int len) throws IOException {
//is here just to implement ObjectInput
readFully(b,off,len);
return len;
}
public long skip(long n) throws IOException {
//is here just to implement ObjectInput
pos += n;
return n;
}
public void close() throws IOException {
//is here just to implement ObjectInput
//do nothing
}
public void writeObject(Object obj) throws IOException {
//is here just to implement ObjectOutput
serializer.serialize(this,obj,objectStack);
}
public void flush() throws IOException {
//is here just to implement ObjectOutput
//do nothing
}
}

View File

@ -1,215 +0,0 @@
///*
//package org.apache.jdbm;
//
//import java.io.DataInput;
//import java.io.DataOutput;
//import java.io.IOException;
//import java.nio.Buffer;
//import java.nio.ByteBuffer;
//import java.util.Arrays;
//
//*/
///**
// * Utility class which implements DataInput and DataOutput on top of ByteBuffer
// * with minimal overhead
// * This class is not used, is left here in case we would ever need it.
// *
// * @author Jan Kotek
// *//*
//
//class DataInputOutput2 implements DataInput, DataOutput {
//
// private ByteBuffer buf;
//
//
// public DataInputOutput2() {
// buf = ByteBuffer.allocate(8);
// }
//
// public DataInputOutput2(ByteBuffer data) {
// buf = data;
// }
//
// public DataInputOutput2(byte[] data) {
// buf = ByteBuffer.wrap(data);
// }
//
//
// public int getPos() {
// return buf.position();
// }
//
//
// public void reset() {
// buf.rewind();
// }
//
//
// public void reset(byte[] b) {
// buf = ByteBuffer.wrap(b);
// }
//
// public void resetForReading() {
// buf.flip();
// }
//
//
// public byte[] toByteArray() {
// byte[] d = new byte[buf.position()];
// buf.position(0);
// buf.get(d); //reading N bytes restores to current position
//
// return d;
// }
//
// public int available() {
// return buf.remaining();
// }
//
//
// public void readFully(byte[] b) throws IOException {
// readFully(b, 0, b.length);
// }
//
// public void readFully(byte[] b, int off, int len) throws IOException {
// buf.get(b,off,len);
// }
//
// public int skipBytes(int n) throws IOException {
// buf.position(buf.position()+n);
// return n;
// }
//
// public boolean readBoolean() throws IOException {
// return buf.get()==1;
// }
//
// public byte readByte() throws IOException {
// return buf.get();
// }
//
// public int readUnsignedByte() throws IOException {
// return buf.get() & 0xff;
// }
//
// public short readShort() throws IOException {
// return buf.getShort();
// }
//
// public int readUnsignedShort() throws IOException {
// return (((int) (buf.get() & 0xff) << 8) |
// ((int) (buf.get() & 0xff) << 0));
// }
//
// public char readChar() throws IOException {
// return (char) readInt();
// }
//
// public int readInt() throws IOException {
// return buf.getInt();
// }
//
// public long readLong() throws IOException {
// return buf.getLong();
// }
//
// public float readFloat() throws IOException {
// return buf.getFloat();
// }
//
// public double readDouble() throws IOException {
// return buf.getDouble();
// }
//
// public String readLine() throws IOException {
// return readUTF();
// }
//
// public String readUTF() throws IOException {
// return Serialization.deserializeString(this);
// }
//
// */
///**
// * make sure there will be enough space in buffer to write N bytes
// *//*
//
// private void ensureAvail(int n) {
// int pos = buf.position();
// if (pos + n >= buf.limit()) {
// int newSize = Math.max(pos + n, buf.limit() * 2);
// byte[] b = new byte[newSize];
// buf.get(b);
// buf = ByteBuffer.wrap(b);
// buf.position(pos);
// }
// }
//
//
// public void write(final int b) throws IOException {
// ensureAvail(1);
// buf.put((byte) b);
// }
//
// public void write(final byte[] b) throws IOException {
// write(b, 0, b.length);
// }
//
// public void write(final byte[] b, final int off, final int len) throws IOException {
// ensureAvail(len);
// buf.put(b,off,len);
// }
//
// public void writeBoolean(final boolean v) throws IOException {
// ensureAvail(1);
// buf.put((byte) (v?1:0));
// }
//
// public void writeByte(final int v) throws IOException {
// ensureAvail(1);
// buf.put((byte) v);
// }
//
// public void writeShort(final short v) throws IOException {
// ensureAvail(2);
// buf.putShort(v);
// }
//
// public void writeChar(final int v) throws IOException {
// writeInt(v);
// }
//
// public void writeInt(final int v) throws IOException {
// ensureAvail(4);
// buf.putInt(v);
// }
//
// public void writeLong(final long v) throws IOException {
// ensureAvail(8);
// buf.putLong(v);
// }
//
// public void writeFloat(final float v) throws IOException {
// ensureAvail(4);
// buf.putFloat(v);
// }
//
// public void writeDouble(final double v) throws IOException {
// ensureAvail(8);
// buf.putDouble(v);
// }
//
// public void writeBytes(String s) throws IOException {
// writeUTF(s);
// }
//
// public void writeChars(String s) throws IOException {
// writeUTF(s);
// }
//
// public void writeUTF(String s) throws IOException {
// Serialization.serializeString(this, s);
// }
//
//}
//*/

View File

@ -1,542 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Persistent HashMap implementation for DB.
* Implemented as an H*Tree structure.
*
* @author Alex Boisvert
* @author Jan Kotek
*/
class HTree<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
final Serializer SERIALIZER = new Serializer<Object>() {
public Object deserialize(DataInput ds2) throws IOException {
DataInputOutput ds = (DataInputOutput) ds2;
try {
int i = ds.readUnsignedByte();
if (i == SerializationHeader.HTREE_BUCKET) { //is HashBucket?
HTreeBucket ret = new HTreeBucket(HTree.this);
if (loadValues)
ret.readExternal(ds);
if (loadValues && ds.available() != 0)
throw new InternalError("bytes left: " + ds.available());
return ret;
} else if (i == SerializationHeader.HTREE_DIRECTORY) {
HTreeDirectory ret = new HTreeDirectory(HTree.this);
ret.readExternal(ds);
if (loadValues && ds.available() != 0)
throw new InternalError("bytes left: " + ds.available());
return ret;
} else {
throw new InternalError("Wrong HTree header: " + i);
}
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
public void serialize(DataOutput out, Object obj) throws IOException {
if (obj instanceof HTreeBucket) {
out.write(SerializationHeader.HTREE_BUCKET);
HTreeBucket b = (HTreeBucket) obj;
b.writeExternal(out);
} else {
out.write(SerializationHeader.HTREE_DIRECTORY);
HTreeDirectory n = (HTreeDirectory) obj;
n.writeExternal(out);
}
}
};
final protected ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* Listeners which are notified about changes in records
*/
protected RecordListener[] recordListeners = new RecordListener[0];
/**
* Serializer used to serialize index keys (optional)
*/
protected Serializer<K> keySerializer;
/**
* Serializer used to serialize index values (optional)
*/
protected Serializer<V> valueSerializer;
protected boolean readonly = false;
final long rootRecid;
DBAbstract db;
/** if false map contains only keys, used for set*/
boolean hasValues = true;
/**
* counts structural changes in tree at runtume. Is here to support fail-fast behaviour.
*/
int modCount;
/**
* indicates if values should be loaded during deserialization, set to true during defragmentation
*/
private boolean loadValues = true;
public Serializer<K> getKeySerializer() {
return keySerializer;
}
public Serializer<V> getValueSerializer() {
return valueSerializer;
}
/**
* cache writing buffer, so it does not have to be allocated on each write
*/
AtomicReference<DataInputOutput> writeBufferCache = new AtomicReference<DataInputOutput>();
/**
* Create a persistent hashtable.
*/
public HTree(DBAbstract db, Serializer<K> keySerializer, Serializer<V> valueSerializer, boolean hasValues)
throws IOException {
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.db = db;
this.hasValues = hasValues;
HTreeDirectory<K, V> root = new HTreeDirectory<K, V>(this, (byte) 0);
root.setPersistenceContext(0);
this.rootRecid = db.insert(root, this.SERIALIZER,false);
}
/**
* Load a persistent hashtable
*/
public HTree(DBAbstract db,long rootRecid, Serializer<K> keySerializer, Serializer<V> valueSerializer, boolean hasValues)
throws IOException {
this.db = db;
this.rootRecid = rootRecid;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.hasValues = hasValues;
}
void setPersistenceContext(DBAbstract db) {
this.db = db;
}
public V put(K key, V value) {
if (readonly)
throw new UnsupportedOperationException("readonly");
lock.writeLock().lock();
try {
if (key == null || value == null)
throw new NullPointerException("Null key or value");
V oldVal = (V) getRoot().put(key, value);
if (oldVal == null) {
modCount++;
//increase size
HTreeDirectory root = getRoot();
root.size++;
db.update(rootRecid,root,SERIALIZER);
for (RecordListener<K, V> r : recordListeners)
r.recordInserted(key, value);
} else {
//notify listeners
for (RecordListener<K, V> r : recordListeners)
r.recordUpdated(key, oldVal, value);
}
return oldVal;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public V get(Object key) {
if (key == null)
return null;
lock.readLock().lock();
try {
return getRoot().get((K) key);
} catch (ClassCastException e) {
return null;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.readLock().unlock();
}
}
public V remove(Object key) {
if (readonly)
throw new UnsupportedOperationException("readonly");
lock.writeLock().lock();
try {
if (key == null)
return null;
V val = (V) getRoot().remove(key);
modCount++;
if (val != null){
//decrease size
HTreeDirectory root = getRoot();
root.size--;
db.update(rootRecid,root,SERIALIZER);
for (RecordListener r : recordListeners)
r.recordRemoved(key, val);
}
return val;
} catch (ClassCastException e) {
return null;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public boolean containsKey(Object key) {
if (key == null)
return false;
//no need for locking, get is already locked
V v = get((K) key);
return v != null;
}
public void clear() {
lock.writeLock().lock();
try {
Iterator<K> keyIter = keys();
while (keyIter.hasNext()) {
keyIter.next();
keyIter.remove();
}
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
/**
* Returns an enumeration of the keys contained in this
*/
public Iterator<K> keys()
throws IOException {
lock.readLock().lock();
try{
return getRoot().keys();
}finally {
lock.readLock().unlock();
}
}
public DBAbstract getRecordManager() {
return db;
}
/**
* add RecordListener which is notified about record changes
*
* @param listener
*/
public void addRecordListener(RecordListener<K, V> listener) {
recordListeners = Arrays.copyOf(recordListeners, recordListeners.length + 1);
recordListeners[recordListeners.length - 1] = listener;
}
/**
* remove RecordListener which is notified about record changes
*
* @param listener
*/
public void removeRecordListener(RecordListener<K, V> listener) {
List l = Arrays.asList(recordListeners);
l.remove(listener);
recordListeners = (RecordListener[]) l.toArray(new RecordListener[1]);
}
public Set<Entry<K, V>> entrySet() {
return _entrySet;
}
private Set<Entry<K, V>> _entrySet = new AbstractSet<Entry<K, V>>() {
protected Entry<K, V> newEntry(K k, V v) {
return new SimpleEntry<K, V>(k, v) {
private static final long serialVersionUID = 978651696969194154L;
public V setValue(V arg0) {
//put is already locked
HTree.this.put(getKey(), arg0);
return super.setValue(arg0);
}
};
}
public boolean add(java.util.Map.Entry<K, V> e) {
if (readonly)
throw new UnsupportedOperationException("readonly");
if (e.getKey() == null)
throw new NullPointerException("Can not add null key");
lock.writeLock().lock();
try{
if (e.getValue().equals(get(e.getKey())))
return false;
HTree.this.put(e.getKey(), e.getValue());
return true;
}finally {
lock.writeLock().unlock();
}
}
@SuppressWarnings("unchecked")
public boolean contains(Object o) {
if (o instanceof Entry) {
Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
//get is already locked
if (e.getKey() != null && HTree.this.get(e.getKey()) != null)
return true;
}
return false;
}
public Iterator<java.util.Map.Entry<K, V>> iterator() {
try {
final Iterator<K> br = keys();
return new Iterator<Entry<K, V>>() {
public boolean hasNext() {
return br.hasNext();
}
public java.util.Map.Entry<K, V> next() {
K k = br.next();
return newEntry(k, get(k));
}
public void remove() {
if (readonly)
throw new UnsupportedOperationException("readonly");
br.remove();
}
};
} catch (IOException e) {
throw new IOError(e);
}
}
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
if (readonly)
throw new UnsupportedOperationException("readonly");
if (o instanceof Entry) {
Entry<K, V> e = (java.util.Map.Entry<K, V>) o;
//check for nulls
if (e.getKey() == null || e.getValue() == null)
return false;
lock.writeLock().lock();
try{
//get old value, must be same as item in entry
V v = get(e.getKey());
if (v == null || !e.getValue().equals(v))
return false;
HTree.this.remove(e.getKey());
return true;
}finally{
lock.writeLock().unlock();
}
}
return false;
}
@Override
public int size() {
lock.readLock().lock();
try {
int counter = 0;
Iterator<K> it = keys();
while (it.hasNext()) {
it.next();
counter++;
}
return counter;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.readLock().unlock();
}
}
};
HTreeDirectory<K, V> getRoot() {
//assumes that caller already holds read or write lock
try {
HTreeDirectory<K, V> root = (HTreeDirectory<K, V>) db.fetch(rootRecid, this.SERIALIZER);
root.setPersistenceContext(rootRecid);
return root;
} catch (IOException e) {
throw new IOError(e);
}
}
public static HTree deserialize(DataInput is, Serialization ser) throws IOException, ClassNotFoundException {
long rootRecid = LongPacker.unpackLong(is);
boolean hasValues = is.readBoolean();
Serializer keySerializer = (Serializer) ser.deserialize(is);
Serializer valueSerializer = (Serializer) ser.deserialize(is);
return new HTree(ser.db,rootRecid, keySerializer, valueSerializer, hasValues);
}
void serialize(DataOutput out) throws IOException {
LongPacker.packLong(out, rootRecid);
out.writeBoolean(hasValues);;
db.defaultSerializer().serialize(out, keySerializer);
db.defaultSerializer().serialize(out, valueSerializer);
}
static void defrag(Long recid, DBStore r1, DBStore r2) throws IOException {
//TODO should modCount be increased after defrag, revert or commit?
try {
byte[] data = r1.fetchRaw(recid);
r2.forceInsert(recid, data);
DataInput in = new DataInputStream(new ByteArrayInputStream(data));
HTree t = (HTree) r1.defaultSerializer().deserialize(in);
t.db = r1;
t.loadValues = false;
HTreeDirectory d = t.getRoot();
if (d != null) {
r2.forceInsert(t.rootRecid, r1.fetchRaw(t.rootRecid));
d.defrag(r1, r2);
}
} catch (ClassNotFoundException e) {
throw new IOError(e);
}
}
public int size(){
return (int) getRoot().size;
}
public boolean hasValues() {
return hasValues;
}
public V putIfAbsent(K key, V value) {
lock.writeLock().lock();
try{
if (!containsKey(key))
return put(key, value);
else
return get(key);
}finally {
lock.writeLock().unlock();
}
}
public boolean remove(Object key, Object value) {
lock.writeLock().lock();
try{
if (containsKey(key) && get(key).equals(value)) {
remove(key);
return true;
} else return false;
}finally {
lock.writeLock().unlock();
}
}
public boolean replace(K key, V oldValue, V newValue) {
lock.writeLock().lock();
try{
if (containsKey(key) && get(key).equals(oldValue)) {
put(key, newValue);
return true;
} else return false;
}finally {
lock.writeLock().unlock();
}
}
public V replace(K key, V value) {
lock.writeLock().lock();
try{
if (containsKey(key)) {
return put(key, value);
} else return null;
}finally {
lock.writeLock().unlock();
}
}
}

View File

@ -1,352 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
import java.util.ArrayList;
/**
* A bucket is a placeholder for multiple (key, value) pairs. Buckets
* are used to store collisions (same hash value) at all levels of an
* H*tree.
* <p/>
* There are two types of buckets: leaf and non-leaf.
* <p/>
* Non-leaf buckets are buckets which hold collisions which happen
* when the H*tree is not fully expanded. Keys in a non-leaf buckets
* can have different hash codes. Non-leaf buckets are limited to an
* arbitrary size. When this limit is reached, the H*tree should create
* a new HTreeDirectory node and distribute keys of the non-leaf buckets into
* the newly created HTreeDirectory.
* <p/>
* A leaf bucket is a bucket which contains keys which all have
* the same <code>hashCode()</code>. Leaf buckets stand at the
* bottom of an H*tree because the hashing algorithm cannot further
* discriminate between different keys based on their hash code.
*
* @author Alex Boisvert
*/
final class HTreeBucket<K, V> {
/**
* The maximum number of elements (key, value) a non-leaf bucket
* can contain.
*/
public static final int OVERFLOW_SIZE = 16;
/**
* Depth of this bucket.
*/
private byte _depth;
/**
* Keys and values in this bucket. Keys are followed by values at KEYPOS+OVERFLOW_SIZE
*/
private Object[] _keysAndValues;
private byte size = 0;
private final HTree<K, V> tree;
/**
* Public constructor for serialization.
*/
public HTreeBucket(HTree<K, V> tree) {
this.tree = tree;
}
/**
* Construct a bucket with a given depth level. Depth level is the
* number of <code>HashDirectory</code> above this bucket.
*/
public HTreeBucket(HTree<K, V> tree, byte level) {
this.tree = tree;
if (level > HTreeDirectory.MAX_DEPTH + 1) {
throw new IllegalArgumentException(
"Cannot create bucket with depth > MAX_DEPTH+1. "
+ "Depth=" + level);
}
_depth = level;
_keysAndValues = new Object[OVERFLOW_SIZE * 2];
}
/**
* Returns the number of elements contained in this bucket.
*/
public int getElementCount() {
return size;
}
/**
* Returns whether or not this bucket is a "leaf bucket".
*/
public boolean isLeaf() {
return (_depth > HTreeDirectory.MAX_DEPTH);
}
/**
* Returns true if bucket can accept at least one more element.
*/
public boolean hasRoom() {
if (isLeaf()) {
return true; // leaf buckets are never full
} else {
// non-leaf bucket
return (size < OVERFLOW_SIZE);
}
}
/**
* Add an element (key, value) to this bucket. If an existing element
* has the same key, it is replaced silently.
*
* @return Object which was previously associated with the given key
* or <code>null</code> if no association existed.
*/
public V addElement(K key, V value) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
// replace existing element
Object before = _keysAndValues[existing + OVERFLOW_SIZE];
if (before instanceof BTreeLazyRecord) {
BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) before;
before = rec.get();
rec.delete();
}
_keysAndValues[existing + OVERFLOW_SIZE] = value;
return (V) before;
} else {
// add new (key, value) pair
_keysAndValues[size] = key;
_keysAndValues[size + OVERFLOW_SIZE] = value;
size++;
return null;
}
}
/**
* Remove an element, given a specific key.
*
* @param key Key of the element to remove
* @return Removed element value, or <code>null</code> if not found
*/
public V removeElement(K key) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
Object o = _keysAndValues[existing + OVERFLOW_SIZE];
if (o instanceof BTreeLazyRecord) {
BTreeLazyRecord<V> rec = (BTreeLazyRecord<V>) o;
o = rec.get();
rec.delete();
}
//move last element to existing
size--;
_keysAndValues[existing] = _keysAndValues[size];
_keysAndValues[existing + OVERFLOW_SIZE] = _keysAndValues[size + OVERFLOW_SIZE];
//and unset last element
_keysAndValues[size] = null;
_keysAndValues[size + OVERFLOW_SIZE] = null;
return (V) o;
} else {
// not found
return null;
}
}
/**
* Returns the value associated with a given key. If the given key
* is not found in this bucket, returns <code>null</code>.
*/
public V getValue(K key) {
//find entry
byte existing = -1;
for (byte i = 0; i < size; i++) {
if (key.equals(_keysAndValues[i])) {
existing = i;
break;
}
}
if (existing != -1) {
Object o = _keysAndValues[existing + OVERFLOW_SIZE];
if (o instanceof BTreeLazyRecord)
return ((BTreeLazyRecord<V>) o).get();
else
return (V) o;
} else {
// key not found
return null;
}
}
/**
* Obtain keys contained in this buckets. Keys are ordered to match
* their values, which be be obtained by calling <code>getValues()</code>.
* <p/>
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList<K> getKeys() {
ArrayList<K> ret = new ArrayList<K>();
for (byte i = 0; i < size; i++) {
ret.add((K) _keysAndValues[i]);
}
return ret;
}
/**
* Obtain values contained in this buckets. Values are ordered to match
* their keys, which be be obtained by calling <code>getKeys()</code>.
* <p/>
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList<V> getValues() {
ArrayList<V> ret = new ArrayList<V>();
for (byte i = 0; i < size; i++) {
ret.add((V) _keysAndValues[i + OVERFLOW_SIZE]);
}
return ret;
}
public void writeExternal(DataOutput out)
throws IOException {
out.write(_depth);
out.write(size);
DataInputOutput out3 = tree.writeBufferCache.getAndSet(null);
if (out3 == null)
out3 = new DataInputOutput();
else
out3.reset();
Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
out3.reset();
keySerializer.serialize(out3, _keysAndValues[i]);
LongPacker.packInt(out, out3.getPos());
out.write(out3.getBuf(), 0, out3.getPos());
}
//write values
if(tree.hasValues()){
Serializer valSerializer = tree.valueSerializer != null ? tree.valueSerializer : tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
Object value = _keysAndValues[i + OVERFLOW_SIZE];
if (value == null) {
out.write(BTreeLazyRecord.NULL);
} else if (value instanceof BTreeLazyRecord) {
out.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(out, ((BTreeLazyRecord) value).recid);
} else {
//transform to byte array
out3.reset();
valSerializer.serialize(out3, value);
if (out3.getPos() > BTreeLazyRecord.MAX_INTREE_RECORD_SIZE) {
//store as separate record
long recid = tree.getRecordManager().insert(out3.toByteArray(), BTreeLazyRecord.FAKE_SERIALIZER,true);
out.write(BTreeLazyRecord.LAZY_RECORD);
LongPacker.packLong(out, recid);
} else {
out.write(out3.getPos());
out.write(out3.getBuf(), 0, out3.getPos());
}
}
}
}
tree.writeBufferCache.set(out3);
}
public void readExternal(DataInputOutput in) throws IOException, ClassNotFoundException {
_depth = in.readByte();
size = in.readByte();
//read keys
Serializer keySerializer = tree.keySerializer != null ? tree.keySerializer : tree.getRecordManager().defaultSerializer();
_keysAndValues = (K[]) new Object[OVERFLOW_SIZE * 2];
for (byte i = 0; i < size; i++) {
int expectedSize = LongPacker.unpackInt(in);
K key = (K) BTreeLazyRecord.fastDeser(in, keySerializer, expectedSize);
_keysAndValues[i] = key;
}
//read values
if(tree.hasValues()){
Serializer<V> valSerializer = tree.valueSerializer != null ? tree.valueSerializer : (Serializer<V>) tree.getRecordManager().defaultSerializer();
for (byte i = 0; i < size; i++) {
int header = in.readUnsignedByte();
if (header == BTreeLazyRecord.NULL) {
_keysAndValues[i + OVERFLOW_SIZE] = null;
} else if (header == BTreeLazyRecord.LAZY_RECORD) {
long recid = LongPacker.unpackLong(in);
_keysAndValues[i + OVERFLOW_SIZE] = (new BTreeLazyRecord(tree.getRecordManager(), recid, valSerializer));
} else {
_keysAndValues[i + OVERFLOW_SIZE] = BTreeLazyRecord.fastDeser(in, valSerializer, header);
}
}
}else{
for (byte i = 0; i < size; i++) {
if(_keysAndValues[i]!=null)
_keysAndValues[i+OVERFLOW_SIZE] = Utils.EMPTY_STRING;
}
}
}
}

View File

@ -1,618 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
import java.util.*;
/**
* Hashtable directory page.
*
* @author Alex Boisvert
*/
final class HTreeDirectory<K, V> {
/**
* Maximum number of children in a directory.
* <p/>
* (Must be a power of 2 -- if you update this value, you must also
* update BIT_SIZE and MAX_DEPTH.)
* <p/>
* !!!! do not change this, it affects storage format, there are also magic numbers which relies on 255 !!!
*/
static final int MAX_CHILDREN = 256;
/**
* Number of significant bits per directory level.
*/
static final int BIT_SIZE = 8; // log2(256) = 8
/**
* Maximum number of levels (zero-based)
* <p/>
* (4 * 8 bits = 32 bits, which is the size of an "int", and as
* you know, hashcodes in Java are "ints")
*/
static final int MAX_DEPTH = 3; // 4 levels
/**
* Record ids of children nodes.
* It is saved in matrix to save memory, some subarrays may be null.
*/
private long[][] _children;
/**
* Depth of this directory page, zero-based
*/
private byte _depth;
/**
* This directory's record ID in the DB. (transient)
*/
private long _recid;
/** if this is root (depth=0), it contains size, otherwise -1*/
long size;
protected final HTree<K, V> tree;
/**
* Public constructor used by serialization
*/
public HTreeDirectory(HTree<K, V> tree) {
this.tree = tree;
}
/**
* Construct a HashDirectory
*
* @param depth Depth of this directory node.
*/
HTreeDirectory(HTree<K, V> tree, byte depth) {
this.tree = tree;
_depth = depth;
_children = new long[32][];
}
/**
* Sets persistence context. This method must be called before any
* persistence-related operation.
*
* @param recid Record id of this directory.
*/
void setPersistenceContext(long recid) {
this._recid = recid;
}
/**
* Get the record identifier used to load this hashtable.
*/
long getRecid() {
return _recid;
}
/**
* Returns whether or not this directory is empty. A directory
* is empty when it no longer contains buckets or sub-directories.
*/
boolean isEmpty() {
for (int i = 0; i < _children.length; i++) {
long[] sub = _children[i];
if (sub!=null){
for (int j = 0; j < 8; j++) {
if(sub[j] != 0) {
return false;
}
}
}
}
return true;
}
/**
* Returns the value which is associated with the given key. Returns
* <code>null</code> if there is not association for this key.
*
* @param key key whose associated value is to be returned
*/
V get(K key)
throws IOException {
int hash = hashCode(key);
long child_recid = getRecid(hash);
if (child_recid == 0) {
// not bucket/node --> not found
return null;
} else {
Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
// System.out.println("HashDirectory.get() child is : "+node);
if (node instanceof HTreeDirectory) {
// recurse into next directory level
HTreeDirectory<K, V> dir = (HTreeDirectory<K, V>) node;
dir.setPersistenceContext(child_recid);
return dir.get(key);
} else {
// node is a bucket
HTreeBucket<K, V> bucket = (HTreeBucket) node;
return bucket.getValue(key);
}
}
}
private long getRecid(int hash) {
long[] sub = _children[hash>>>3];
return sub==null? 0 : sub[hash%8];
}
private void putRecid(int hash, long recid) {
long[] sub = _children[hash>>>3];
if(sub == null){
sub = new long[8];
_children[hash>>>3] = sub;
}
sub[hash%8] = recid;
}
/**
* Associates the specified value with the specified key.
*
* @param key key with which the specified value is to be assocated.
* @param value value to be associated with the specified key.
* @return object which was previously associated with the given key,
* or <code>null</code> if no association existed.
*/
Object put(final Object key, final Object value)
throws IOException {
if (value == null) {
return remove(key);
}
int hash = hashCode(key);
long child_recid = getRecid(hash);
if (child_recid == 0) {
// no bucket/node here yet, let's create a bucket
HTreeBucket bucket = new HTreeBucket(tree, (byte) (_depth + 1));
// insert (key,value) pair in bucket
Object existing = bucket.addElement(key, value);
long b_recid = tree.db.insert(bucket, tree.SERIALIZER,false);
putRecid(hash, b_recid);
tree.db.update(_recid, this, tree.SERIALIZER);
// System.out.println("Added: "+bucket);
return existing;
} else {
Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
if (node instanceof HTreeDirectory) {
// recursive insert in next directory level
HTreeDirectory dir = (HTreeDirectory) node;
dir.setPersistenceContext(child_recid);
return dir.put(key, value);
} else {
// node is a bucket
HTreeBucket bucket = (HTreeBucket) node;
if (bucket.hasRoom()) {
Object existing = bucket.addElement(key, value);
tree.db.update(child_recid, bucket, tree.SERIALIZER);
// System.out.println("Added: "+bucket);
return existing;
} else {
// overflow, so create a new directory
if (_depth == MAX_DEPTH) {
throw new RuntimeException("Cannot create deeper directory. "
+ "Depth=" + _depth);
}
HTreeDirectory dir = new HTreeDirectory(tree, (byte) (_depth + 1));
long dir_recid = tree.db.insert(dir, tree.SERIALIZER,false);
dir.setPersistenceContext(dir_recid);
putRecid(hash, dir_recid);
tree.db.update(_recid, this, tree.SERIALIZER);
// discard overflown bucket
tree.db.delete(child_recid);
// migrate existing bucket elements
ArrayList keys = bucket.getKeys();
ArrayList values = bucket.getValues();
int entries = keys.size();
for (int i = 0; i < entries; i++) {
dir.put(keys.get(i), values.get(i));
}
// (finally!) insert new element
return dir.put(key, value);
}
}
}
}
/**
* Remove the value which is associated with the given key. If the
* key does not exist, this method simply ignores the operation.
*
* @param key key whose associated value is to be removed
* @return object which was associated with the given key, or
* <code>null</code> if no association existed with given key.
*/
Object remove(Object key) throws IOException {
int hash = hashCode(key);
long child_recid = getRecid(hash);
if (child_recid == 0) {
// not bucket/node --> not found
return null;
} else {
Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
// System.out.println("HashDirectory.remove() child is : "+node);
if (node instanceof HTreeDirectory) {
// recurse into next directory level
HTreeDirectory dir = (HTreeDirectory) node;
dir.setPersistenceContext(child_recid);
Object existing = dir.remove(key);
if (existing != null) {
if (dir.isEmpty()) {
// delete empty directory
tree.db.delete(child_recid);
putRecid(hash, 0);
tree.db.update(_recid, this, tree.SERIALIZER);
}
}
return existing;
} else {
// node is a bucket
HTreeBucket bucket = (HTreeBucket) node;
Object existing = bucket.removeElement(key);
if (existing != null) {
if (bucket.getElementCount() >= 1) {
tree.db.update(child_recid, bucket, tree.SERIALIZER);
} else {
// delete bucket, it's empty
tree.db.delete(child_recid);
putRecid(hash, 0);
tree.db.update(_recid, this, tree.SERIALIZER);
}
}
return existing;
}
}
}
/**
* Calculates the hashcode of a key, based on the current directory
* depth.
*/
private int hashCode(Object key) {
int hashMask = hashMask();
int hash = key.hashCode();
hash = hash & hashMask;
hash = hash >>> ((MAX_DEPTH - _depth) * BIT_SIZE);
hash = hash % MAX_CHILDREN;
/*
System.out.println("HashDirectory.hashCode() is: 0x"
+Integer.toHexString(hash)
+" for object hashCode() 0x"
+Integer.toHexString(key.hashCode()));
*/
return hash;
}
/**
* Calculates the hashmask of this directory. The hashmask is the
* bit mask applied to a hashcode to retain only bits that are
* relevant to this directory level.
*/
int hashMask() {
int bits = MAX_CHILDREN - 1;
int hashMask = bits << ((MAX_DEPTH - _depth) * BIT_SIZE);
/*
System.out.println("HashDirectory.hashMask() is: 0x"
+Integer.toHexString(hashMask));
*/
return hashMask;
}
/**
* Returns an enumeration of the keys contained in this
*/
Iterator<K> keys()
throws IOException {
return new HDIterator(true);
}
/**
* Returns an enumeration of the values contained in this
*/
Iterator<V> values()
throws IOException {
return new HDIterator(false);
}
public void writeExternal(DataOutput out)
throws IOException {
out.writeByte(_depth);
if(_depth==0){
LongPacker.packLong(out,size);
}
int zeroStart = 0;
for (int i = 0; i < MAX_CHILDREN; i++) {
if (getRecid(i) != 0) {
zeroStart = i;
break;
}
}
out.write(zeroStart);
if (zeroStart == MAX_CHILDREN)
return;
int zeroEnd = 0;
for (int i = MAX_CHILDREN - 1; i >= 0; i--) {
if (getRecid(i) != 0) {
zeroEnd = i;
break;
}
}
out.write(zeroEnd);
for (int i = zeroStart; i <= zeroEnd; i++) {
LongPacker.packLong(out, getRecid(i));
}
}
public void readExternal(DataInputOutput in)
throws IOException, ClassNotFoundException {
_depth = in.readByte();
if(_depth==0)
size = LongPacker.unpackLong(in);
else
size = -1;
_children = new long[32][];
int zeroStart = in.readUnsignedByte();
int zeroEnd = in.readUnsignedByte();
for (int i = zeroStart; i <= zeroEnd; i++) {
long recid = LongPacker.unpackLong(in);
if(recid!=0)
putRecid(i,recid);
}
}
public void defrag(DBStore r1, DBStore r2) throws IOException, ClassNotFoundException {
for (long[] sub: _children) {
if(sub==null) continue;
for (long child : sub) {
if (child == 0) continue;
byte[] data = r1.fetchRaw(child);
r2.forceInsert(child, data);
Object t = tree.SERIALIZER.deserialize(new DataInputOutput(data));
if (t instanceof HTreeDirectory) {
((HTreeDirectory) t).defrag(r1, r2);
}
}
}
}
void deleteAllChildren() throws IOException {
for(long[] ll : _children){
if(ll!=null){
for(long l:ll ){
if(l!=0){
tree.db.delete(l);
}
}
}
}
}
////////////////////////////////////////////////////////////////////////
// INNER CLASS
////////////////////////////////////////////////////////////////////////
/**
* Utility class to enumerate keys/values in a HTree
*/
class HDIterator<A> implements Iterator<A> {
/**
* True if we're iterating on keys, False if enumerating on values.
*/
private boolean _iterateKeys;
/**
* Stacks of directories & last enumerated child position
*/
private ArrayList _dirStack;
private ArrayList _childStack;
/**
* Current HashDirectory in the hierarchy
*/
private HTreeDirectory _dir;
/**
* Current child position
*/
private int _child;
/**
* Current bucket iterator
*/
private Iterator<A> _iter;
private A next;
/**
* last item returned in next(), is used to remove() last item
*/
private A last;
private int expectedModCount;
/**
* Construct an iterator on this directory.
*
* @param iterateKeys True if iteration supplies keys, False
* if iterateKeys supplies values.
*/
HDIterator(boolean iterateKeys)
throws IOException {
_dirStack = new ArrayList();
_childStack = new ArrayList();
_dir = HTreeDirectory.this;
_child = -1;
_iterateKeys = iterateKeys;
expectedModCount = tree.modCount;
prepareNext();
next = next2();
}
/**
* Returns the next object.
*/
public A next2() {
A next = null;
if (_iter != null && _iter.hasNext()) {
next = _iter.next();
} else {
try {
prepareNext();
} catch (IOException except) {
throw new IOError(except);
}
if (_iter != null && _iter.hasNext()) {
return next2();
}
}
return next;
}
/**
* Prepare internal state so we can answer <code>hasMoreElements</code>
* <p/>
* Actually, this code prepares an Enumeration on the next
* Bucket to enumerate. If no following bucket is found,
* the next Enumeration is set to <code>null</code>.
*/
private void prepareNext() throws IOException {
long child_recid = 0;
// get next bucket/directory to enumerate
do {
_child++;
if (_child >= MAX_CHILDREN) {
if (_dirStack.isEmpty()) {
// no more directory in the stack, we're finished
return;
}
// try next node
_dir = (HTreeDirectory) _dirStack.remove(_dirStack.size() - 1);
_child = ((Integer) _childStack.remove(_childStack.size() - 1)).intValue();
continue;
}
child_recid = _dir.getRecid(_child);
} while (child_recid == 0);
if (child_recid == 0) {
throw new Error("child_recid cannot be 0");
}
Object node = tree.db.fetch(child_recid, tree.SERIALIZER);
// System.out.println("HDEnumeration.get() child is : "+node);
if (node instanceof HTreeDirectory) {
// save current position
_dirStack.add(_dir);
_childStack.add(new Integer(_child));
_dir = (HTreeDirectory) node;
_child = -1;
// recurse into
_dir.setPersistenceContext(child_recid);
prepareNext();
} else {
// node is a bucket
HTreeBucket bucket = (HTreeBucket) node;
if (_iterateKeys) {
ArrayList keys2 = bucket.getKeys();
_iter = keys2.iterator();
} else {
_iter = bucket.getValues().iterator();
}
}
}
public boolean hasNext() {
return next != null;
}
public A next() {
if (next == null) throw new NoSuchElementException();
if (expectedModCount != tree.modCount)
throw new ConcurrentModificationException();
last = next;
next = next2();
return last;
}
public void remove() {
if (last == null) throw new IllegalStateException();
if (expectedModCount != tree.modCount)
throw new ConcurrentModificationException();
//TODO current delete behaviour may change node layout. INVESTIGATE if this can happen!
tree.remove(last);
last = null;
expectedModCount++;
}
}
}

View File

@ -1,47 +0,0 @@
package org.apache.jdbm;
import java.util.AbstractSet;
import java.util.Iterator;
/**
* Wrapper for HTree to implement java.util.Map interface
*/
class HTreeSet<E> extends AbstractSet<E> {
final HTree<E, Object> map;
HTreeSet(HTree map) {
this.map = map;
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(E e) {
return map.put(e, Utils.EMPTY_STRING) == null;
}
public boolean remove(Object o) {
return map.remove(o) == Utils.EMPTY_STRING;
}
public void clear() {
map.clear();
}
}

View File

@ -1,480 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdbm;
import java.io.*;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* LinkedList2 which stores its nodes on disk.
*
* @author Jan Kotek
*/
class LinkedList2<E> extends AbstractSequentialList<E> {
private DBAbstract db;
final long rootRecid;
/** size limit, is not currently used, but needs to be here for future compatibility.
* Zero means no limit.
*/
long sizeLimit = 0;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static final class Root{
long first;
long last;
long size;
}
private static final Serializer<Root> ROOT_SERIALIZER= new Serializer<Root>(){
public void serialize(DataOutput out, Root obj) throws IOException {
LongPacker.packLong(out,obj.first);
LongPacker.packLong(out,obj.last);
LongPacker.packLong(out,obj.size);
}
public Root deserialize(DataInput in) throws IOException, ClassNotFoundException {
Root r = new Root();
r.first = LongPacker.unpackLong(in);
r.last = LongPacker.unpackLong(in);
r.size = LongPacker.unpackLong(in);
return r;
}
};
private Serializer<E> valueSerializer;
/**
* indicates that entry values should not be loaded during deserialization, used during defragmentation
*/
protected boolean loadValues = true;
/** constructor used for deserialization */
LinkedList2(DBAbstract db,long rootRecid, Serializer<E> valueSerializer) {
this.db = db;
this.rootRecid = rootRecid;
this.valueSerializer = valueSerializer;
}
/** constructor used to create new empty list*/
LinkedList2(DBAbstract db, Serializer<E> valueSerializer) throws IOException {
this.db = db;
if (valueSerializer != null && !(valueSerializer instanceof Serializable))
throw new IllegalArgumentException("Serializer does not implement Serializable");
this.valueSerializer = valueSerializer;
//create root
this.rootRecid = db.insert(new Root(), ROOT_SERIALIZER,false);
}
void setPersistenceContext(DBAbstract db) {
this.db = db;
}
public ListIterator<E> listIterator(int index) {
lock.readLock().lock();
try{
Root r = getRoot();
if (index < 0 || index > r.size)
throw new IndexOutOfBoundsException();
Iter iter = new Iter();
iter.next = r.first;
//scroll to requested position
//TODO scroll from end, if beyond half
for (int i = 0; i < index; i++) {
iter.next();
}
return iter;
}finally {
lock.readLock().unlock();
}
}
Root getRoot(){
//expect that caller already holds lock
try {
return db.fetch(rootRecid,ROOT_SERIALIZER);
} catch (IOException e) {
throw new IOError(e);
}
}
public int size() {
lock.readLock().lock();
try{
return (int) getRoot().size;
}finally {
lock.readLock().unlock();
}
}
public Iterator<E> descendingIterator() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public boolean add(Object value) {
lock.writeLock().lock();
try {
Root r = getRoot();
Entry e = new Entry(r.last, 0, value);
long recid = db.insert(e, entrySerializer,false);
//update old last Entry to point to new record
if (r.last != 0) {
Entry oldLast = db.fetch(r.last, entrySerializer);
if (oldLast.next != 0) throw new Error();
oldLast.next = recid;
db.update(r.last, oldLast, entrySerializer);
}
//update linked list
r.last = recid;
if (r.first == 0) r.first = recid;
r.size++;
db.update(rootRecid, r, ROOT_SERIALIZER);
modCount++;
return true;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
private Entry<E> fetch(long recid) {
lock.readLock().lock();
try {
return db.fetch(recid, entrySerializer);
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.readLock().unlock();
}
}
/**
* called from Serialization object
*/
static LinkedList2 deserialize(DataInput is, Serialization ser) throws IOException, ClassNotFoundException {
long rootrecid = LongPacker.unpackLong(is);
long sizeLimit = LongPacker.unpackLong(is);
if(sizeLimit!=0) throw new InternalError("LinkedList.sizeLimit not supported in this JDBM version");
Serializer serializer = (Serializer) ser.deserialize(is);
return new LinkedList2(ser.db,rootrecid, serializer);
}
void serialize(DataOutput out) throws IOException {
LongPacker.packLong(out, rootRecid);
LongPacker.packLong(out, sizeLimit);
db.defaultSerializer().serialize(out, valueSerializer);
}
private final Serializer<Entry> entrySerializer = new Serializer<Entry>() {
public void serialize(DataOutput out, Entry e) throws IOException {
LongPacker.packLong(out, e.prev);
LongPacker.packLong(out, e.next);
if (valueSerializer != null)
valueSerializer.serialize(out, (E) e.value);
else
db.defaultSerializer().serialize(out, e.value);
}
public Entry<E> deserialize(DataInput in) throws IOException, ClassNotFoundException {
long prev = LongPacker.unpackLong(in);
long next = LongPacker.unpackLong(in);
Object value = null;
if (loadValues)
value = valueSerializer == null ? db.defaultSerializer().deserialize(in) : valueSerializer.deserialize(in);
return new LinkedList2.Entry(prev, next, value);
}
};
static class Entry<E> {
long prev = 0;
long next = 0;
E value;
public Entry(long prev, long next, E value) {
this.prev = prev;
this.next = next;
this.value = value;
}
}
private final class Iter implements ListIterator<E> {
private int expectedModCount = modCount;
private int index = 0;
private long prev = 0;
private long next = 0;
private byte lastOper = 0;
public boolean hasNext() {
return next != 0;
}
public E next() {
if (next == 0) throw new NoSuchElementException();
checkForComodification();
Entry<E> e = fetch(next);
prev = next;
next = e.next;
index++;
lastOper = +1;
return e.value;
}
public boolean hasPrevious() {
return prev != 0;
}
public E previous() {
checkForComodification();
Entry<E> e = fetch(prev);
next = prev;
prev = e.prev;
index--;
lastOper = -1;
return e.value;
}
public int nextIndex() {
return index;
}
public int previousIndex() {
return index - 1;
}
public void remove() {
checkForComodification();
lock.writeLock().lock();
try {
if (lastOper == 1) {
//last operation was next() so remove previous element
lastOper = 0;
Entry<E> p = db.fetch(prev, entrySerializer);
//update entry before previous
if (p.prev != 0) {
Entry<E> pp = db.fetch(p.prev, entrySerializer);
pp.next = p.next;
db.update(p.prev, pp, entrySerializer);
}
//update entry after next
if (p.next != 0) {
Entry<E> pn = db.fetch(p.next, entrySerializer);
pn.prev = p.prev;
db.update(p.next, pn, entrySerializer);
}
//remove old record from db
db.delete(prev);
//update list
Root r = getRoot();
if (r.first == prev)
r.first = next;
if (r.last == prev)
r.last = next;
r.size--;
db.update(rootRecid, r,ROOT_SERIALIZER);
modCount++;
expectedModCount++;
//update iterator
prev = p.prev;
} else if (lastOper == -1) {
//last operation was prev() so remove next element
lastOper = 0;
Entry<E> n = db.fetch(next, entrySerializer);
//update entry before next
if (n.prev != 0) {
Entry<E> pp = db.fetch(n.prev, entrySerializer);
pp.next = n.next;
db.update(n.prev, pp, entrySerializer);
}
//update entry after previous
if (n.next != 0) {
Entry<E> pn = db.fetch(n.next, entrySerializer);
pn.prev = n.prev;
db.update(n.next, pn, entrySerializer);
}
//remove old record from db
db.delete(next);
//update list
Root r = getRoot();
if (r.last == next)
r.last = prev;
if (r.first == next)
r.first = prev;
r.size--;
db.update(rootRecid, r,ROOT_SERIALIZER);
modCount++;
expectedModCount++;
//update iterator
next = n.next;
} else
throw new IllegalStateException();
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public void set(E value) {
checkForComodification();
lock.writeLock().lock();
try {
if (lastOper == 1) {
//last operation was next(), so update previous item
lastOper = 0;
Entry<E> n = db.fetch(prev, entrySerializer);
n.value = value;
db.update(prev, n, entrySerializer);
} else if (lastOper == -1) {
//last operation was prev() so update next item
lastOper = 0;
Entry<E> n = db.fetch(next, entrySerializer);
n.value = value;
db.update(next, n, entrySerializer);
} else
throw new IllegalStateException();
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public void add(E value) {
checkForComodification();
//use more efficient method if possible
if (next == 0) {
LinkedList2.this.add(value);
expectedModCount++;
return;
}
lock.writeLock().lock();
try {
//insert new entry
Entry<E> e = new Entry<E>(prev, next, value);
long recid = db.insert(e, entrySerializer,false);
//update previous entry
if (prev != 0) {
Entry<E> p = db.fetch(prev, entrySerializer);
if (p.next != next) throw new Error();
p.next = recid;
db.update(prev, p, entrySerializer);
}
//update next entry
Entry<E> n = fetch(next);
if (n.prev != prev) throw new Error();
n.prev = recid;
db.update(next, n, entrySerializer);
//update List
Root r = getRoot();
r.size++;
db.update(rootRecid, r, ROOT_SERIALIZER);
//update iterator
expectedModCount++;
modCount++;
prev = recid;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* Copyes collection from one db to other, while keeping logical recids unchanged
*/
static void defrag(long recid, DBStore r1, DBStore r2) throws IOException {
try {
//move linked list itself
byte[] data = r1.fetchRaw(recid);
r2.forceInsert(recid, data);
DataInputOutput in = new DataInputOutput();
in.reset(data);
LinkedList2 l = (LinkedList2) r1.defaultSerializer().deserialize(in);
l.loadValues = false;
//move linkedlist root
if(l.rootRecid == 0) //empty list, done
return;
data = r1.fetchRaw(l.rootRecid);
r2.forceInsert(l.rootRecid, data);
in.reset(data);
Root r = ROOT_SERIALIZER.deserialize(in);
//move all other nodes in linked list
long current = r.first;
while (current != 0) {
data = r1.fetchRaw(current);
in.reset(data);
r2.forceInsert(current, data);
Entry e = (Entry) l.entrySerializer.deserialize(in);
current = e.next;
}
} catch (ClassNotFoundException e) {
throw new IOError(e);
}
}
}

View File

@ -1,239 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOException;
import java.util.Arrays;
/**
* This class manages the linked lists of logical rowid pages.
*/
final class LogicalRowIdManager {
// our record file and associated page manager
private final PageFile file;
private final PageManager pageman;
static final short ELEMS_PER_PAGE = (short) ((Storage.PAGE_SIZE - Magic.PAGE_HEADER_SIZE) / Magic.PhysicalRowId_SIZE);
private long[] freeRecordsInTransRowid = new long[4];
private int freeRecordsInTransSize = 0;
/** number of free logical rowids on logical free page, is SHORT*/
static final int OFFSET_FREE_COUNT = Magic.PAGE_HEADER_SIZE;
static final int FREE_HEADER_SIZE = Magic.PAGE_HEADER_SIZE + Magic.SZ_SHORT;
/** maximal number of free logical per page */
static final int FREE_RECORDS_PER_PAGE = (Storage.PAGE_SIZE -FREE_HEADER_SIZE)/6;
/**
* Creates a log rowid manager using the indicated record file and page manager
*/
LogicalRowIdManager(PageFile file, PageManager pageman) throws IOException {
this.file = file;
this.pageman = pageman;
}
/**
* Creates a new logical rowid pointing to the indicated physical id
*
* @param physloc physical location to point to
* @return logical recid
*/
long insert(final long physloc) throws IOException {
// check whether there's a free rowid to reuse
long retval = getFreeSlot();
if (retval == 0) {
// no. This means that we bootstrap things by allocating
// a new translation page and freeing all the rowids on it.
long firstPage = pageman.allocate(Magic.TRANSLATION_PAGE);
short curOffset = Magic.PAGE_HEADER_SIZE;
for (int i = 0; i < ELEMS_PER_PAGE; i++) {
putFreeSlot(((-firstPage) << Storage.PAGE_SIZE_SHIFT) + (long) curOffset);
curOffset += Magic.PhysicalRowId_SIZE;
}
retval = getFreeSlot();
if (retval == 0) {
throw new Error("couldn't obtain free translation");
}
}
// write the translation.
update(retval, physloc);
return retval;
}
/**
* Insert at forced location, use only for defragmentation !!
*
* @param logicalRowId
* @param physLoc
* @throws IOException
*/
void forceInsert(final long logicalRowId, final long physLoc) throws IOException {
if (fetch(logicalRowId) != 0)
throw new Error("can not forceInsert, record already exists: " + logicalRowId);
update(logicalRowId, physLoc);
}
/**
* Releases the indicated logical rowid.
*/
void delete(final long logicalrowid) throws IOException {
//zero out old location, is needed for defragmentation
final long pageId = -(logicalrowid>>> Storage.PAGE_SIZE_SHIFT);
final PageIo xlatPage = file.get(pageId);
xlatPage.pageHeaderSetLocation((short) (logicalrowid & Storage.OFFSET_MASK), 0);
file.release(pageId, true);
putFreeSlot(logicalrowid);
}
/**
* Updates the mapping
*
* @param logicalrowid The logical rowid
* @param physloc The physical rowid
*/
void update(final long logicalrowid, final long physloc) throws IOException {
final long pageId = -(logicalrowid>>> Storage.PAGE_SIZE_SHIFT);
final PageIo xlatPage = file.get(pageId);
xlatPage.pageHeaderSetLocation((short) (logicalrowid & Storage.OFFSET_MASK), physloc);
file.release(pageId, true);
}
/**
* Returns a mapping
*
* @param logicalrowid The logical rowid
* @return The physical rowid, 0 if does not exist
*/
long fetch(long logicalrowid) throws IOException {
final long pageId = -(logicalrowid>>> Storage.PAGE_SIZE_SHIFT);
final long last = pageman.getLast(Magic.TRANSLATION_PAGE);
if (last - 1 > pageId)
return 0;
final short offset = (short) (logicalrowid & Storage.OFFSET_MASK);
final PageIo xlatPage = file.get(pageId);
final long ret = xlatPage.pageHeaderGetLocation(offset);
file.release(pageId, false);
return ret;
}
void commit() throws IOException {
if(freeRecordsInTransSize==0) return;
long freeRecPageId = pageman.getLast(Magic.FREELOGIDS_PAGE);
if(freeRecPageId == 0){
//allocate new
freeRecPageId = pageman.allocate(Magic.FREELOGIDS_PAGE);
}
PageIo freeRecPage = file.get(freeRecPageId);
//write all uncommited free records
for(int rowPos = 0;rowPos<freeRecordsInTransSize;rowPos++){
short count = freeRecPage.readShort(OFFSET_FREE_COUNT);
if(count == FREE_RECORDS_PER_PAGE){
//allocate new free recid page
file.release(freeRecPage);
freeRecPageId = pageman.allocate(Magic.FREELOGIDS_PAGE);
freeRecPage = file.get(freeRecPageId);
freeRecPage.writeShort(FREE_RECORDS_PER_PAGE, (short)0);
count = 0;
}
final int offset = (count ) *6 + FREE_HEADER_SIZE;
//write free recid and increase counter
freeRecPage.writeSixByteLong(offset,freeRecordsInTransRowid[rowPos]);
count++;
freeRecPage.writeShort(OFFSET_FREE_COUNT, count);
}
file.release(freeRecPage);
clearFreeRecidsInTransaction();
}
private void clearFreeRecidsInTransaction() {
if(freeRecordsInTransRowid.length>128)
freeRecordsInTransRowid = new long[4];
freeRecordsInTransSize = 0;
}
void rollback() throws IOException {
clearFreeRecidsInTransaction();
}
/**
* Returns a free Logical rowid, or
* 0 if nothing was found.
*/
long getFreeSlot() throws IOException {
if (freeRecordsInTransSize != 0) {
return freeRecordsInTransRowid[--freeRecordsInTransSize];
}
final long logicFreePageId = pageman.getLast(Magic.FREELOGIDS_PAGE);
if(logicFreePageId == 0) {
return 0;
}
PageIo logicFreePage = file.get(logicFreePageId);
short recCount = logicFreePage.readShort(OFFSET_FREE_COUNT);
if(recCount <= 0){
throw new InternalError();
}
final int offset = (recCount -1) *6 + FREE_HEADER_SIZE;
final long ret = logicFreePage.readSixByteLong(offset);
recCount--;
if(recCount>0){
//decrease counter and zero out old record
logicFreePage.writeSixByteLong(offset,0);
logicFreePage.writeShort(OFFSET_FREE_COUNT, recCount);
file.release(logicFreePage);
}else{
//release this page
file.release(logicFreePage);
pageman.free(Magic.FREELOGIDS_PAGE,logicFreePageId);
}
return ret;
}
/**
* Puts the indicated rowid on the free list
*/
void putFreeSlot(long rowid) throws IOException {
//ensure capacity
if(freeRecordsInTransSize == freeRecordsInTransRowid.length)
freeRecordsInTransRowid = Arrays.copyOf(freeRecordsInTransRowid, freeRecordsInTransRowid.length * 4);
//add record and increase size
freeRecordsInTransRowid[freeRecordsInTransSize]=rowid;
freeRecordsInTransSize++;
}
}

View File

@ -1,432 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdbm;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Hash Map which uses primitive long as key.
* Main advantage is new instanceof of Long does not have to be created for each lookup.
* <p/>
* This code comes from Android, which in turns comes from Apache Harmony.
* This class was modified to use primitive longs and stripped down to consume less space.
* <p/>
* Author of JDBM modifications: Jan Kotek
*/
class LongHashMap<V> implements Serializable {
private static final long serialVersionUID = 362499999763181265L;
private int elementCount;
private Entry<V>[] elementData;
private final float loadFactor;
private int threshold;
private int defaultSize = 16;
private transient Entry<V> reuseAfterDelete = null;
static final class Entry<V> implements Serializable{
private static final long serialVersionUID = 362445231113181265L;
Entry<V> next;
V value;
long key;
Entry(long theKey) {
this.key = theKey;
this.value = null;
}
}
static class HashMapIterator<V> implements Iterator<V> {
private int position = 0;
boolean canRemove = false;
Entry<V> entry;
Entry<V> lastEntry;
final LongHashMap<V> associatedMap;
HashMapIterator(LongHashMap<V> hm) {
associatedMap = hm;
}
public boolean hasNext() {
if (entry != null) {
return true;
}
Entry<V>[] elementData = associatedMap.elementData;
int length = elementData.length;
int newPosition = position;
boolean result = false;
while (newPosition < length) {
if (elementData[newPosition] == null) {
newPosition++;
} else {
result = true;
break;
}
}
position = newPosition;
return result;
}
public V next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Entry<V> result;
Entry<V> _entry = entry;
if (_entry == null) {
result = lastEntry = associatedMap.elementData[position++];
entry = lastEntry.next;
} else {
if (lastEntry.next != _entry) {
lastEntry = lastEntry.next;
}
result = _entry;
entry = _entry.next;
}
canRemove = true;
return result.value;
}
public void remove() {
if (!canRemove) {
throw new IllegalStateException();
}
canRemove = false;
if (lastEntry.next == entry) {
while (associatedMap.elementData[--position] == null) {
// Do nothing
}
associatedMap.elementData[position] = associatedMap.elementData[position].next;
entry = null;
} else {
lastEntry.next = entry;
}
if (lastEntry != null) {
Entry<V> reuse = lastEntry;
lastEntry = null;
reuse.key = Long.MIN_VALUE;
reuse.value = null;
associatedMap.reuseAfterDelete = reuse;
}
associatedMap.elementCount--;
}
}
@SuppressWarnings("unchecked")
private Entry<V>[] newElementArray(int s) {
return new Entry[s];
}
/**
* Constructs a new empty {@code HashMap} instance.
*
* @since Android 1.0
*/
public LongHashMap() {
this(16);
}
/**
* Constructs a new {@code HashMap} instance with the specified capacity.
*
* @param capacity the initial capacity of this hash map.
* @throws IllegalArgumentException when the capacity is less than zero.
* @since Android 1.0
*/
public LongHashMap(int capacity) {
defaultSize = capacity;
if (capacity >= 0) {
elementCount = 0;
elementData = newElementArray(capacity == 0 ? 1 : capacity);
loadFactor = 0.75f; // Default load factor of 0.75
computeMaxSize();
} else {
throw new IllegalArgumentException();
}
}
// BEGIN android-changed
/**
* Removes all mappings from this hash map, leaving it empty.
*
* @see #isEmpty
* @see #size
* @since Android 1.0
*/
public void clear() {
if (elementCount > 0) {
elementCount = 0;
}
if(elementData.length>1024 && elementData.length>defaultSize)
elementData = new Entry[defaultSize];
else
Arrays.fill(elementData, null);
computeMaxSize();
}
// END android-changed
/**
* Returns a shallow copy of this map.
*
* @return a shallow copy of this map.
* @since Android 1.0
*/
private void computeMaxSize() {
threshold = (int) (elementData.length * loadFactor);
}
/**
* Returns the value of the mapping with the specified key.
*
* @param key the key.
* @return the value of the mapping with the specified key, or {@code null}
* if no mapping for the specified key is found.
* @since Android 1.0
*/
public V get(final long key) {
final int hash = powerHash(key);
final int index = (hash & 0x7FFFFFFF) % elementData.length;
//find non null entry
Entry<V> m = elementData[index];
while (m != null) {
if (key == m.key)
return m.value;
m = m.next;
}
return null;
}
/**
* Returns whether this map is empty.
*
* @return {@code true} if this map has no elements, {@code false}
* otherwise.
* @see #size()
* @since Android 1.0
*/
public boolean isEmpty() {
return elementCount == 0;
}
/**
* @return iterator over keys
*/
// public Iterator<K> keyIterator(){
// return new HashMapIterator<K, K, V>(
// new MapEntry.Type<K, K, V>() {
// public K get(Entry<K, V> entry) {
// return entry.key;
// }
// }, HashMap.this);
//
// }
/**
* Maps the specified key to the specified value.
*
* @param key the key.
* @param value the value.
* @return the value of any previous mapping with the specified key or
* {@code null} if there was no such mapping.
* @since Android 1.0
*/
public V put(final long key, final V value) {
int hash = powerHash(key);
int index = (hash & 0x7FFFFFFF) % elementData.length;
//find non null entry
Entry<V> entry = elementData[index];
while (entry != null && key != entry.key) {
entry = entry.next;
}
if (entry == null) {
if (++elementCount > threshold) {
rehash();
index = (hash & 0x7FFFFFFF) % elementData.length;
}
entry = createHashedEntry(key, index);
}
V result = entry.value;
entry.value = value;
return result;
}
Entry<V> createHashedEntry(final long key, final int index) {
Entry<V> entry = reuseAfterDelete;
if (entry == null) {
entry = new Entry<V>(key);
} else {
reuseAfterDelete = null;
entry.key = key;
entry.value = null;
}
entry.next = elementData[index];
elementData[index] = entry;
return entry;
}
void rehash(final int capacity) {
int length = (capacity == 0 ? 1 : capacity << 1);
Entry<V>[] newData = newElementArray(length);
for (int i = 0; i < elementData.length; i++) {
Entry<V> entry = elementData[i];
while (entry != null) {
int index = ((int) powerHash(entry.key) & 0x7FFFFFFF) % length;
Entry<V> next = entry.next;
entry.next = newData[index];
newData[index] = entry;
entry = next;
}
}
elementData = newData;
computeMaxSize();
}
void rehash() {
rehash(elementData.length);
}
/**
* Removes the mapping with the specified key from this map.
*
* @param key the key of the mapping to remove.
* @return the value of the removed mapping or {@code null} if no mapping
* for the specified key was found.
* @since Android 1.0
*/
public V remove(final long key) {
Entry<V> entry = removeEntry(key);
if (entry == null)
return null;
V ret = entry.value;
entry.value = null;
entry.key = Long.MIN_VALUE;
reuseAfterDelete = entry;
return ret;
}
Entry<V> removeEntry(final long key) {
Entry<V> last = null;
final int hash = powerHash(key);
final int index = (hash & 0x7FFFFFFF) % elementData.length;
Entry<V> entry = elementData[index];
while (true) {
if (entry == null) {
return null;
}
if (key == entry.key) {
if (last == null) {
elementData[index] = entry.next;
} else {
last.next = entry.next;
}
elementCount--;
return entry;
}
last = entry;
entry = entry.next;
}
}
/**
* Returns the number of elements in this map.
*
* @return the number of elements in this map.
* @since Android 1.0
*/
public int size() {
return elementCount;
}
/**
* @returns iterator over values in map
*/
public Iterator<V> valuesIterator() {
return new HashMapIterator<V>(this);
}
static final private int powerHash(final long key){
int h = (int)(key ^ (key >>> 32));
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
}

View File

@ -1,106 +0,0 @@
/*
Copyright (c) 2008, Nathan Sweet
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.apache.jdbm;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* Packing utility for non-negative <code>long</code> and values.
* <p/>
* Originally developed for Kryo by Nathan Sweet.
* Modified for JDBM by Jan Kotek
*/
public final class LongPacker {
/**
* Pack non-negative long into output stream.
* It will occupy 1-10 bytes depending on value (lower values occupy smaller space)
*
* @param os
* @param value
* @throws IOException
*/
static public void packLong(DataOutput os, long value) throws IOException {
if (value < 0) {
throw new IllegalArgumentException("negative value: v=" + value);
}
while ((value & ~0x7FL) != 0) {
os.write((((int) value & 0x7F) | 0x80));
value >>>= 7;
}
os.write((byte) value);
}
/**
* Unpack positive long value from the input stream.
*
* @param is The input stream.
* @return The long value.
* @throws java.io.IOException
*/
static public long unpackLong(DataInput is) throws IOException {
long result = 0;
for (int offset = 0; offset < 64; offset += 7) {
long b = is.readUnsignedByte();
result |= (b & 0x7F) << offset;
if ((b & 0x80) == 0) {
return result;
}
}
throw new Error("Malformed long.");
}
/**
* Pack non-negative long into output stream.
* It will occupy 1-5 bytes depending on value (lower values occupy smaller space)
*
* @param os
* @param value
* @throws IOException
*/
static public void packInt(DataOutput os, int value) throws IOException {
if (value < 0) {
throw new IllegalArgumentException("negative value: v=" + value);
}
while ((value & ~0x7F) != 0) {
os.write(((value & 0x7F) | 0x80));
value >>>= 7;
}
os.write((byte) value);
}
static public int unpackInt(DataInput is) throws IOException {
for (int offset = 0, result = 0; offset < 32; offset += 7) {
int b = is.readUnsignedByte();
result |= (b & 0x7F) << offset;
if ((b & 0x80) == 0) {
return result;
}
}
throw new Error("Malformed integer.");
}
}

View File

@ -1,105 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
/**
* This interface contains magic cookies.
*/
interface Magic {
/**
* Magic cookie at start of file
*/
short FILE_HEADER = 0x1350;
/**
* Magic for pages. They're offset by the page type magic codes.
*/
short PAGE_MAGIC = 0x1351;
/**
* Magics for pages in certain lists.
*/
short FREE_PAGE = 0;
short USED_PAGE = 1;
short TRANSLATION_PAGE = 2;
short FREELOGIDS_PAGE = 3;
short FREEPHYSIDS_PAGE = 4;
short FREEPHYSIDS_ROOT_PAGE = 5;
/**
* Number of lists in a file
*/
short NLISTS = 6;
/**
* Magic for transaction file
*/
short LOGFILE_HEADER = 0x1360;
/**
* Size of an externalized byte
*/
short SZ_BYTE = 1;
/**
* Size of an externalized short
*/
short SZ_SHORT = 2;
/**
* Size of an externalized int
*/
short SZ_INT = 4;
/**
* Size of an externalized long
*/
short SZ_LONG = 8;
/**
* size of three byte integer
*/
short SZ_SIX_BYTE_LONG = 6;
/**offsets in file header (zero page in file)*/
short FILE_HEADER_O_MAGIC = 0; // short magic
short FILE_HEADER_O_LISTS = Magic.SZ_SHORT; // long[2*NLISTS]
int FILE_HEADER_O_ROOTS = FILE_HEADER_O_LISTS + (Magic.NLISTS * 2 * Magic.SZ_LONG);
/**
* The number of "root" rowids available in the file.
*/
int FILE_HEADER_NROOTS = 16;
short PAGE_HEADER_O_MAGIC = 0; // short magic
short PAGE_HEADER_O_NEXT = Magic.SZ_SHORT;
short PAGE_HEADER_O_PREV = PAGE_HEADER_O_NEXT + Magic.SZ_SIX_BYTE_LONG;
short PAGE_HEADER_SIZE = PAGE_HEADER_O_PREV + Magic.SZ_SIX_BYTE_LONG;
short PhysicalRowId_O_LOCATION = 0; // long page
// short PhysicalRowId_O_OFFSET = Magic.SZ_SIX_BYTE_LONG; // short offset
int PhysicalRowId_SIZE = Magic.SZ_SIX_BYTE_LONG;
short DATA_PAGE_O_FIRST = PAGE_HEADER_SIZE; // short firstrowid
short DATA_PAGE_O_DATA = (short) (DATA_PAGE_O_FIRST + Magic.SZ_SHORT);
short DATA_PER_PAGE = (short) (Storage.PAGE_SIZE - DATA_PAGE_O_DATA);
}

View File

@ -1,26 +0,0 @@
package org.apache.jdbm;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.util.ArrayList;
/**
* An alternative to <code>java.io.ObjectInputStream</code> which uses more efficient serialization
*/
public class ObjectInputStream2 extends DataInputStream implements ObjectInput {
public ObjectInputStream2(InputStream in) {
super(in);
}
public Object readObject() throws ClassNotFoundException, IOException {
//first read class data
ArrayList<SerialClassInfo.ClassInfo> info = SerialClassInfo.serializer.deserialize(this);
Serialization ser = new Serialization(null,0,info);
return ser.deserialize(this);
}
}

View File

@ -1,25 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.util.ArrayList;
/**
* An alternative to <code>java.io.ObjectOutputStream</code> which uses more efficient serialization
*/
public class ObjectOutputStream2 extends DataOutputStream implements ObjectOutput {
public ObjectOutputStream2(OutputStream out) {
super(out);
}
public void writeObject(Object obj) throws IOException {
ArrayList registered = new ArrayList();
Serialization ser = new Serialization(null,0,registered);
byte[] data = ser.serialize(obj);
//write class info first
SerialClassInfo.serializer.serialize(this, registered);
//and write data
write(data);
}
}

View File

@ -1,390 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.IOError;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;
/**
* This class represents a random access file as a set of fixed size
* records. Each record has a physical record number, and records are
* cached in order to improve access.
* <p/>
* The set of dirty records on the in-use list constitutes a transaction.
* Later on, we will send these records to some recovery thingy.
* <p/>
* PageFile is splited between more files, each with max size 1GB.
*/
final class PageFile {
final PageTransactionManager txnMgr;
/**
* Pages currently locked for read/update ops. When released the page goes
* to the dirty or clean list, depending on a flag. The file header page is
* normally locked plus the page that is currently being read or modified.
*
* @see PageIo#isDirty()
*/
private final LongHashMap<PageIo> inUse = new LongHashMap<PageIo>();
/**
* Pages whose state is dirty.
*/
private final LongHashMap<PageIo> dirty = new LongHashMap<PageIo>();
/**
* Pages in a <em>historical</em> transaction(s) that have been written
* onto the log but which have not yet been committed to the database.
*/
private final LongHashMap<PageIo> inTxn = new LongHashMap<PageIo>();
// transactions disabled?
final boolean transactionsDisabled;
/**
* A array of clean data to wipe clean pages.
*/
static final byte[] CLEAN_DATA = new byte[Storage.PAGE_SIZE];
final Storage storage;
private Cipher cipherOut;
private Cipher cipherIn;
/**
* Creates a new object on the indicated filename. The file is
* opened in read/write mode.
*
* @param fileName the name of the file to open or create, without
* an extension.
* @throws IOException whenever the creation of the underlying
* RandomAccessFile throws it.
*/
PageFile(String fileName, boolean readonly, boolean transactionsDisabled, Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile, boolean lockingDisabled) throws IOException {
this.cipherIn = cipherIn;
this.cipherOut = cipherOut;
this.transactionsDisabled = transactionsDisabled;
if(fileName == null){
this.storage = new StorageMemory(transactionsDisabled);
}else if(DBMaker.isZipFileLocation(fileName)!=null)
this.storage = new StorageZip(DBMaker.isZipFileLocation(fileName));
// }else if (fileName.contains("!/"))
// this.storage = new StorageZip(fileName);
else if(useRandomAccessFile)
this.storage = new StorageDisk(fileName,readonly,lockingDisabled);
else
this.storage = new StorageDiskMapped(fileName,readonly,transactionsDisabled,lockingDisabled);
if (this.storage.isReadonly() && !readonly)
throw new IllegalArgumentException("This type of storage is readonly, you should call readonly() on DBMaker");
if (!readonly && !transactionsDisabled) {
txnMgr = new PageTransactionManager(this, storage, cipherIn, cipherOut);
} else {
txnMgr = null;
}
}
public PageFile(String filename) throws IOException {
this(filename, false, false, null, null,false,false);
}
/**
* Gets a page from the file. The returned byte array is
* the in-memory copy of the record, and thus can be written
* (and subsequently released with a dirty flag in order to
* write the page back). If transactions are disabled, changes
* may be written directly
*
* @param pageId The record number to retrieve.
*/
PageIo get(long pageId) throws IOException {
// try in transaction list, dirty list, free list
PageIo node = inTxn.get(pageId);
if (node != null) {
inTxn.remove(pageId);
inUse.put(pageId, node);
return node;
}
node = dirty.get(pageId);
if (node != null) {
dirty.remove(pageId);
inUse.put(pageId, node);
return node;
}
// sanity check: can't be on in use list
if (inUse.get(pageId) != null) {
throw new Error("double get for page " + pageId);
}
//read node from file
if (cipherOut == null) {
node = new PageIo(pageId,storage.read(pageId));
} else {
//decrypt if needed
ByteBuffer b = storage.read(pageId);
byte[] bb;
if(b.hasArray()){
bb = b.array();
}else{
bb = new byte[Storage.PAGE_SIZE];
b.position(0);
b.get(bb, 0, Storage.PAGE_SIZE);
}
if (!Utils.allZeros(bb)) try {
bb = cipherOut.doFinal(bb);
node = new PageIo(pageId, ByteBuffer.wrap(bb));
} catch (Exception e) {
throw new IOError(e);
}else {
node = new PageIo(pageId, ByteBuffer.wrap(PageFile.CLEAN_DATA).asReadOnlyBuffer());
}
}
inUse.put(pageId, node);
node.setClean();
return node;
}
/**
* Releases a page.
*
* @param pageId The record number to release.
* @param isDirty If true, the page was modified since the get().
*/
void release(final long pageId, final boolean isDirty) throws IOException {
final PageIo page = inUse.remove(pageId);
if (!page.isDirty() && isDirty)
page.setDirty();
if (page.isDirty()) {
dirty.put(pageId, page);
} else if (!transactionsDisabled && page.isInTransaction()) {
inTxn.put(pageId, page);
}
}
/**
* Releases a page.
*
* @param page The page to release.
*/
void release(final PageIo page) throws IOException {
final long key = page.getPageId();
inUse.remove(key);
if (page.isDirty()) {
// System.out.println( "Dirty: " + key + page );
dirty.put(key, page);
} else if (!transactionsDisabled && page.isInTransaction()) {
inTxn.put(key, page);
}
}
/**
* Discards a page (will not write the page even if it's dirty)
*
* @param page The page to discard.
*/
void discard(PageIo page) {
long key = page.getPageId();
inUse.remove(key);
}
/**
* Commits the current transaction by flushing all dirty buffers
* to disk.
*/
void commit() throws IOException {
// debugging...
if (!inUse.isEmpty() && inUse.size() > 1) {
showList(inUse.valuesIterator());
throw new Error("in use list not empty at commit time ("
+ inUse.size() + ")");
}
// System.out.println("committing...");
if (dirty.size() == 0) {
// if no dirty pages, skip commit process
return;
}
if (!transactionsDisabled) {
txnMgr.start();
}
//sort pages by IDs
long[] pageIds = new long[dirty.size()];
int c = 0;
for (Iterator<PageIo> i = dirty.valuesIterator(); i.hasNext(); ) {
pageIds[c] = i.next().getPageId();
c++;
}
Arrays.sort(pageIds);
for (long pageId : pageIds) {
PageIo node = dirty.get(pageId);
// System.out.println("node " + node + " map size now " + dirty.size());
if (transactionsDisabled) {
if(cipherIn !=null)
storage.write(node.getPageId(), ByteBuffer.wrap(Utils.encrypt(cipherIn, node.getData())));
else
storage.write(node.getPageId(),node.getData());
node.setClean();
} else {
txnMgr.add(node);
inTxn.put(node.getPageId(), node);
}
}
dirty.clear();
if (!transactionsDisabled) {
txnMgr.commit();
}
}
/**
* Rollback the current transaction by discarding all dirty buffers
*/
void rollback() throws IOException {
// debugging...
if (!inUse.isEmpty()) {
showList(inUse.valuesIterator());
throw new Error("in use list not empty at rollback time ("
+ inUse.size() + ")");
}
// System.out.println("rollback...");
dirty.clear();
txnMgr.synchronizeLogFromDisk();
if (!inTxn.isEmpty()) {
showList(inTxn.valuesIterator());
throw new Error("in txn list not empty at rollback time ("
+ inTxn.size() + ")");
}
;
}
/**
* Commits and closes file.
*/
void close() throws IOException {
if (!dirty.isEmpty()) {
commit();
}
if(!transactionsDisabled && txnMgr!=null){
txnMgr.shutdown();
}
if (!inTxn.isEmpty()) {
showList(inTxn.valuesIterator());
throw new Error("In transaction not empty");
}
// these actually ain't that bad in a production release
if (!dirty.isEmpty()) {
System.out.println("ERROR: dirty pages at close time");
showList(dirty.valuesIterator());
throw new Error("Dirty pages at close time");
}
if (!inUse.isEmpty()) {
System.out.println("ERROR: inUse pages at close time");
showList(inUse.valuesIterator());
throw new Error("inUse pages at close time");
}
storage.sync();
storage.forceClose();
}
/**
* Force closing the file and underlying transaction manager.
* Used for testing purposed only.
*/
void forceClose() throws IOException {
if(!transactionsDisabled){
txnMgr.forceClose();
}
storage.forceClose();
}
/**
* Prints contents of a list
*/
private void showList(Iterator<PageIo> i) {
int cnt = 0;
while (i.hasNext()) {
System.out.println("elem " + cnt + ": " + i.next());
cnt++;
}
}
/**
* Synchs a node to disk. This is called by the transaction manager's
* synchronization code.
*/
void synch(PageIo node) throws IOException {
ByteBuffer data = node.getData();
if (data != null) {
if(cipherIn!=null)
storage.write(node.getPageId(), ByteBuffer.wrap(Utils.encrypt(cipherIn, data)));
else
storage.write(node.getPageId(), data);
}
}
/**
* Releases a node from the transaction list, if it was sitting
* there.
*/
void releaseFromTransaction(PageIo node)
throws IOException {
inTxn.remove(node.getPageId());
}
/**
* Synchronizes the file.
*/
void sync() throws IOException {
storage.sync();
}
public int getDirtyPageCount() {
return dirty.size();
}
public void deleteAllFiles() throws IOException {
storage.deleteAllFiles();
}
}

View File

@ -1,448 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.*;
import java.nio.ByteBuffer;
import static org.apache.jdbm.Magic.*;
/**
* Wraps a page sizes ByteBuffer for reading and writing.
* <p>
* ByteBuffer may be subview of a larger buffer (ie large buffer mapped over a file).
* In this case ByteBuffer will have set limit, mark and other variables to limit its size.
* <p>
* For reading buffered may be shared. For example StoreMemory just returns its pages without copying.
* In this case buffer is marked as 'readonly' and needs to be copied before write (Copy On Write - COW).
* COW is not necessary if transactions are disabled and changes can not be rolled back.
* <p>
*/
final class PageIo {
private long pageId;
private ByteBuffer data; // work area
/** buffers contains changes which were not written to disk yet. */
private boolean dirty = false;
private int transactionCount = 0;
/**
* Default constructor for serialization
*/
public PageIo() {
// empty
}
/**
* Constructs a new PageIo instance working on the indicated
* buffer.
*/
PageIo(long pageId, byte[] data) {
this.pageId = pageId;
this.data = ByteBuffer.wrap(data);
}
public PageIo(long pageId, ByteBuffer data) {
this.pageId = pageId;
this.data = data;
}
/** Frequent reads on direct buffer may be slower then on heap buffer.
* This method converts native direct to heap buffer
*/
void ensureHeapBuffer(){
if(data.isDirect()){
final byte[] bb = new byte[Storage.PAGE_SIZE];
data.get(bb,0,Storage.PAGE_SIZE);
data = ByteBuffer.wrap(bb);
if(data.isReadOnly()) throw new InternalError();
}
}
/**
* Returns the underlying array
*/
ByteBuffer getData() {
return data;
}
/**
* Returns the page number.
*/
long getPageId() {
return pageId;
}
/**
* Sets the dirty flag
*/
void setDirty() {
dirty = true;
if(data.isReadOnly()){
// make copy if needed, so we can write into buffer
byte[] buf = new byte[Storage.PAGE_SIZE];
data.get(buf,0,Storage.PAGE_SIZE);
data = ByteBuffer.wrap(buf);
}
}
/**
* Clears the dirty flag
*/
void setClean() {
dirty = false;
}
/**
* Returns true if the dirty flag is set.
*/
boolean isDirty() {
return dirty;
}
/**
* Returns true if the block is still dirty with respect to the
* transaction log.
*/
boolean isInTransaction() {
return transactionCount != 0;
}
/**
* Increments transaction count for this block, to signal that this
* block is in the log but not yet in the data file. The method also
* takes a snapshot so that the data may be modified in new transactions.
*/
void incrementTransactionCount() {
transactionCount++;
}
/**
* Decrements transaction count for this block, to signal that this
* block has been written from the log to the data file.
*/
void decrementTransactionCount() {
transactionCount--;
if (transactionCount < 0)
throw new Error("transaction count on page "
+ getPageId() + " below zero!");
}
/**
* Reads a byte from the indicated position
*/
public byte readByte(int pos) {
return data.get(pos);
}
/**
* Writes a byte to the indicated position
*/
public void writeByte(int pos, byte value) {
setDirty();
data.put(pos,value);
}
/**
* Reads a short from the indicated position
*/
public short readShort(int pos) {
return data.getShort(pos);
}
/**
* Writes a short to the indicated position
*/
public void writeShort(int pos, short value) {
setDirty();
data.putShort(pos,value);
}
/**
* Reads an int from the indicated position
*/
public int readInt(int pos) {
return data.getInt(pos);
}
/**
* Writes an int to the indicated position
*/
public void writeInt(int pos, int value) {
setDirty();
data.putInt(pos,value);
}
/**
* Reads a long from the indicated position
*/
public long readLong(int pos) {
return data.getLong(pos);
}
/**
* Writes a long to the indicated position
*/
public void writeLong(int pos, long value) {
setDirty();
data.putLong(pos,value);
}
/**
* Reads a long from the indicated position
*/
public long readSixByteLong(int pos) {
long ret =
((long) (data.get(pos + 0) & 0x7f) << 40) |
((long) (data.get(pos + 1) & 0xff) << 32) |
((long) (data.get(pos + 2) & 0xff) << 24) |
((long) (data.get(pos + 3) & 0xff) << 16) |
((long) (data.get(pos + 4) & 0xff) << 8) |
((long) (data.get(pos + 5) & 0xff) << 0);
if((data.get(pos + 0) & 0x80) != 0)
return -ret;
else
return ret;
}
/**
* Writes a long to the indicated position
*/
public void writeSixByteLong(int pos, long value) {
// if(value<0) throw new IllegalArgumentException();
// if(value >> (6*8)!=0)
// throw new IllegalArgumentException("does not fit");
int negativeBit = 0;
if(value<0){
value = -value;
negativeBit = 0x80;
}
setDirty();
data.put(pos + 0,(byte) ((0x7f & (value >> 40)) | negativeBit));
data.put(pos + 1, (byte) (0xff & (value >> 32)));
data.put(pos + 2, (byte) (0xff & (value >> 24)));
data.put(pos + 3, (byte) (0xff & (value >> 16)));
data.put(pos + 4, (byte) (0xff & (value >> 8)));
data.put(pos + 5, (byte) (0xff & (value >> 0)));
}
// overrides java.lang.Object
public String toString() {
return "PageIo("
+ pageId + ","
+ dirty +")";
}
public void readExternal(DataInputStream in, Cipher cipherOut) throws IOException {
pageId = in.readLong();
byte[] data2 = new byte[Storage.PAGE_SIZE];
in.readFully(data2);
if (cipherOut == null || Utils.allZeros(data2))
data = ByteBuffer.wrap(data2);
else try {
data = ByteBuffer.wrap(cipherOut.doFinal(data2));
} catch (Exception e) {
throw new IOError(e);
}
}
public void writeExternal(DataOutput out, Cipher cipherIn) throws IOException {
out.writeLong(pageId);
out.write(Utils.encrypt(cipherIn, data.array()));
}
public byte[] getByteArray() {
if ( data.hasArray())
return data.array();
byte[] d= new byte[Storage.PAGE_SIZE];
data.rewind();
data.get(d,0,Storage.PAGE_SIZE);
return d;
}
public void writeByteArray(byte[] buf, int srcOffset, int offset, int length) {
setDirty();
data.rewind();
data.position(offset);
data.put(buf,srcOffset,length);
}
public void fileHeaderCheckHead(boolean isNew){
if (isNew)
writeShort(FILE_HEADER_O_MAGIC, Magic.FILE_HEADER);
else{
short magic = readShort(FILE_HEADER_O_MAGIC);
if(magic!=FILE_HEADER)
throw new Error("CRITICAL: file header magic not OK " + magic);
}
}
/**
* Returns the first page of the indicated list
*/
long fileHeaderGetFirstOf(int list) {
return readLong(fileHeaderOffsetOfFirst(list));
}
/**
* Sets the first page of the indicated list
*/
void fileHeaderSetFirstOf(int list, long value) {
writeLong(fileHeaderOffsetOfFirst(list), value);
}
/**
* Returns the last page of the indicated list
*/
long fileHeaderGetLastOf(int list) {
return readLong(fileHeaderOffsetOfLast(list));
}
/**
* Sets the last page of the indicated list
*/
void fileHeaderSetLastOf(int list, long value) {
writeLong(fileHeaderOffsetOfLast(list), value);
}
/**
* Returns the offset of the "first" page of the indicated list
*/
private short fileHeaderOffsetOfFirst(int list) {
return (short) (FILE_HEADER_O_LISTS + (2 * Magic.SZ_LONG * list));
}
/**
* Returns the offset of the "last" page of the indicated list
*/
private short fileHeaderOffsetOfLast(int list) {
return (short) (fileHeaderOffsetOfFirst(list) + Magic.SZ_LONG);
}
/**
* Returns the indicated root rowid. A root rowid is a special rowid
* that needs to be kept between sessions. It could conceivably be
* stored in a special file, but as a large amount of space in the
* page header is wasted anyway, it's more useful to store it where
* it belongs.
*
*/
long fileHeaderGetRoot(final int root) {
final short offset = (short) (FILE_HEADER_O_ROOTS + (root * Magic.SZ_LONG));
return readLong(offset);
}
/**
* Sets the indicated root rowid.
*
*/
void fileHeaderSetRoot(final int root, final long rowid) {
final short offset = (short) (FILE_HEADER_O_ROOTS + (root * Magic.SZ_LONG));
writeLong(offset, rowid);
}
/**
* Returns true if the magic corresponds with the fileHeader magic.
*/
boolean pageHeaderMagicOk() {
int magic = pageHeaderGetMagic();
return magic >= Magic.PAGE_MAGIC && magic <= (Magic.PAGE_MAGIC + Magic.FREEPHYSIDS_ROOT_PAGE);
}
/**
* For paranoia mode
*/
protected void pageHeaderParanoiaMagicOk() {
if (!pageHeaderMagicOk())
throw new Error("CRITICAL: page header magic not OK " + pageHeaderGetMagic());
}
short pageHeaderGetMagic() {
return readShort(PAGE_HEADER_O_MAGIC);
}
long pageHeaderGetNext() {
pageHeaderParanoiaMagicOk();
return readSixByteLong(PAGE_HEADER_O_NEXT);
}
void pageHeaderSetNext(long next) {
pageHeaderParanoiaMagicOk();
writeSixByteLong(PAGE_HEADER_O_NEXT, next);
}
long pageHeaderGetPrev() {
pageHeaderParanoiaMagicOk();
return readSixByteLong(PAGE_HEADER_O_PREV);
}
void pageHeaderSetPrev(long prev) {
pageHeaderParanoiaMagicOk();
writeSixByteLong(PAGE_HEADER_O_PREV, prev);
}
void pageHeaderSetType(short type) {
writeShort(PAGE_HEADER_O_MAGIC, (short) (Magic.PAGE_MAGIC + type));
}
long pageHeaderGetLocation(final short pos){
return readSixByteLong(pos + PhysicalRowId_O_LOCATION);
}
void pageHeaderSetLocation(short pos, long value) {
writeSixByteLong(pos + PhysicalRowId_O_LOCATION, value);
}
short dataPageGetFirst() {
return readShort(DATA_PAGE_O_FIRST);
}
void dataPageSetFirst(short value) {
pageHeaderParanoiaMagicOk();
if (value > 0 && value < DATA_PAGE_O_DATA)
throw new Error("DataPage.setFirst: offset " + value + " too small");
writeShort(DATA_PAGE_O_FIRST, value);
}
}

View File

@ -1,247 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* This class manages the linked lists of pages that make up a file.
*/
final class PageManager {
// our record file
final PageFile file;
private PageIo headerBuf;
/**
* Creates a new page manager using the indicated record file.
*/
PageManager(PageFile file) throws IOException {
this.file = file;
// check the file headerBuf.fileHeader If the magic is 0, we assume a new
// file. Note that we hold on to the file header node.
headerBuf = file.get(0);
headerBuf.ensureHeapBuffer();
headerBuf.fileHeaderCheckHead(headerBuf.readShort(0) == 0);
}
/**
* Allocates a page of the indicated type. Returns recid of the
* page.
*/
long allocate(short type) throws IOException {
if (type == Magic.FREE_PAGE)
throw new Error("allocate of free page?");
// do we have something on the free list?
long retval = headerBuf.fileHeaderGetFirstOf(Magic.FREE_PAGE);
boolean isNew = false;
if(type!=Magic.TRANSLATION_PAGE){
if (retval != 0) {
// yes. Point to it and make the next of that page the
// new first free page.
headerBuf.fileHeaderSetFirstOf(Magic.FREE_PAGE, getNext(retval));
} else {
// nope. make a new record
retval = headerBuf.fileHeaderGetLastOf(Magic.FREE_PAGE);
if (retval == 0)
// very new file - allocate record #1
retval = 1;
headerBuf.fileHeaderSetLastOf(Magic.FREE_PAGE, retval + 1);
isNew = true;
}
}else{
//translation pages have different allocation scheme
//and also have negative address
retval = headerBuf.fileHeaderGetLastOf(Magic.TRANSLATION_PAGE) - 1;
isNew = true;
}
// Cool. We have a record, add it to the correct list
PageIo pageHdr = file.get(retval);
if(isNew){
pageHdr.pageHeaderSetType(type);
}else{
if (!pageHdr.pageHeaderMagicOk())
throw new Error("CRITICAL: page header magic for page "+
pageHdr.getPageId() + " not OK "+ pageHdr.pageHeaderGetMagic());
}
long oldLast = headerBuf.fileHeaderGetLastOf(type);
// Clean data.
pageHdr.writeByteArray(PageFile.CLEAN_DATA, 0, 0, Storage.PAGE_SIZE);
pageHdr.pageHeaderSetType(type);
pageHdr.pageHeaderSetPrev(oldLast);
pageHdr.pageHeaderSetNext(0);
if (oldLast == 0)
// This was the first one of this type
headerBuf.fileHeaderSetFirstOf(type, retval);
headerBuf.fileHeaderSetLastOf(type, retval);
file.release(retval, true);
// If there's a previous, fix up its pointer
if (oldLast != 0) {
pageHdr = file.get(oldLast);
pageHdr.pageHeaderSetNext(retval);
file.release(oldLast, true);
}
return retval;
}
/**
* Frees a page of the indicated type.
*/
void free(short type, long recid) throws IOException {
if (type == Magic.FREE_PAGE)
throw new Error("free free page?");
if (type == Magic.TRANSLATION_PAGE)
throw new Error("Translation page can not be dealocated");
if (recid == 0)
throw new Error("free header page?");
// get the page and read next and previous pointers
PageIo pageHdr = file.get(recid);
long prev = pageHdr.pageHeaderGetPrev();
long next = pageHdr.pageHeaderGetNext();
// put the page at the front of the free list.
pageHdr.pageHeaderSetType(Magic.FREE_PAGE);
pageHdr.pageHeaderSetNext(headerBuf.fileHeaderGetFirstOf(Magic.FREE_PAGE));
pageHdr.pageHeaderSetPrev(0);
headerBuf.fileHeaderSetFirstOf(Magic.FREE_PAGE, recid);
file.release(recid, true);
// remove the page from its old list
if (prev != 0) {
pageHdr = file.get(prev);
pageHdr.pageHeaderSetNext(next);
file.release(prev, true);
} else {
headerBuf.fileHeaderSetFirstOf(type, next);
}
if (next != 0) {
pageHdr = file.get(next);
pageHdr.pageHeaderSetPrev(prev);
file.release(next, true);
} else {
headerBuf.fileHeaderSetLastOf(type, prev);
}
}
/**
* Returns the page following the indicated page
*/
long getNext(long page) throws IOException {
try {
return file.get(page).pageHeaderGetNext();
} finally {
file.release(page, false);
}
}
/**
* Returns the page before the indicated page
*/
long getPrev(long page) throws IOException {
try {
return file.get(page).pageHeaderGetPrev();
} finally {
file.release(page, false);
}
}
/**
* Returns the first page on the indicated list.
*/
long getFirst(short type) throws IOException {
return headerBuf.fileHeaderGetFirstOf(type);
}
/**
* Returns the last page on the indicated list.
*/
long getLast(short type) throws IOException {
return headerBuf.fileHeaderGetLastOf(type);
}
/**
* Commit all pending (in-memory) data by flushing the page manager.
* This forces a flush of all outstanding pages (this it's an implicit
* {@link PageFile#commit} as well).
*/
void commit() throws IOException {
// write the header out
file.release(headerBuf);
file.commit();
// and obtain it again
headerBuf = file.get(0);
headerBuf.ensureHeapBuffer();
headerBuf.fileHeaderCheckHead(headerBuf.readShort(0) == 0);
}
/**
* Flushes the page manager. This forces a flush of all outstanding
* pages (this it's an implicit {@link PageFile#commit} as well).
*/
void rollback() throws IOException {
// release header
file.discard(headerBuf);
file.rollback();
// and obtain it again
headerBuf = file.get(0);
headerBuf.ensureHeapBuffer();
headerBuf.fileHeaderCheckHead(headerBuf.readShort(0) == 0);
}
/**
* Closes the page manager. This flushes the page manager and releases
* the lock on the headerBuf.fileHeader
*/
void close() throws IOException {
file.release(headerBuf);
file.commit();
headerBuf = null;
}
/**
* PageManager permanently locks zero page, and we need this for backups
*/
ByteBuffer getHeaderBufData() {
return headerBuf.getData();
}
public PageIo getFileHeader() {
return headerBuf;
}
}

View File

@ -1,329 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
* This class manages the transaction log that belongs to every
* {@link PageFile}. The transaction log is either clean, or
* in progress. In the latter case, the transaction manager
* takes care of a roll forward.
*/
// TODO: Handle the case where we are recovering lg9 and lg0, were we
// should start with lg9 instead of lg0!
final class PageTransactionManager {
private PageFile owner;
// streams for transaction log.
private DataOutputStream oos;
/**
* In-core copy of transactions. We could read everything back from
* the log file, but the PageFile needs to keep the dirty pages in
* core anyway, so we might as well point to them and spare us a lot
* of hassle.
*/
private ArrayList<PageIo> txn = new ArrayList<PageIo>();
private int curTxn = -1;
private Storage storage;
private Cipher cipherIn;
private Cipher cipherOut;
/**
* Instantiates a transaction manager instance. If recovery
* needs to be performed, it is done.
*
* @param owner the PageFile instance that owns this transaction mgr.
* @param storage
* @param cipherIn
* @param cipherOut
*/
PageTransactionManager(PageFile owner, Storage storage, Cipher cipherIn, Cipher cipherOut) throws IOException {
this.owner = owner;
this.storage = storage;
this.cipherIn = cipherIn;
this.cipherOut = cipherOut;
recover();
open();
}
/**
* Synchronize log file data with the main database file.
* <p/>
* After this call, the main database file is guaranteed to be
* consistent and guaranteed to be the only file needed for
* backup purposes.
*/
public void synchronizeLog()
throws IOException {
synchronizeLogFromMemory();
}
/**
* Synchs in-core transactions to data file and opens a fresh log
*/
private void synchronizeLogFromMemory() throws IOException {
close();
TreeSet<PageIo> pageList = new TreeSet<PageIo>(PAGE_IO_COMPARTOR);
int numPages = 0;
int writtenPages = 0;
if(txn!=null){
// Add each page to the pageList, replacing the old copy of this
// page if necessary, thus avoiding writing the same page twice
for (Iterator<PageIo> k = txn.iterator(); k.hasNext(); ) {
PageIo page = k.next();
if (pageList.contains(page)) {
page.decrementTransactionCount();
} else {
writtenPages++;
boolean result = pageList.add(page);
}
numPages++;
}
txn = null;
}
// Write the page from the pageList to disk
synchronizePages(pageList, true);
owner.sync();
open();
}
/**
* Opens the log file
*/
private void open() throws IOException {
oos = storage.openTransactionLog();
oos.writeShort(Magic.LOGFILE_HEADER);
oos.flush();
curTxn = -1;
}
/**
* Startup recovery on all files
*/
private void recover() throws IOException {
DataInputStream ois = storage.readTransactionLog();
// if transaction log is empty, or does not exist
if (ois == null) return;
while (true) {
ArrayList<PageIo> pages = null;
try {
int size = LongPacker.unpackInt(ois);
pages = new ArrayList<PageIo>(size);
for (int i = 0; i < size; i++) {
PageIo b = new PageIo();
b.readExternal(ois, cipherOut);
pages.add(b);
}
} catch (IOException e) {
// corrupted logfile, ignore rest of transactions
break;
}
synchronizePages(pages, false);
}
owner.sync();
ois.close();
storage.deleteTransactionLog();
}
/**
* Synchronizes the indicated pages with the owner.
*/
private void synchronizePages(Iterable<PageIo> pages, boolean fromCore)
throws IOException {
// write pages vector elements to the data file.
for (PageIo cur : pages) {
owner.synch(cur);
if (fromCore) {
cur.decrementTransactionCount();
if (!cur.isInTransaction()) {
owner.releaseFromTransaction(cur);
}
}
}
}
/**
* Set clean flag on the pages.
*/
private void setClean(ArrayList<PageIo> pages)
throws IOException {
for (PageIo cur : pages) {
cur.setClean();
}
}
/**
* Discards the indicated pages and notify the owner.
*/
private void discardPages(ArrayList<PageIo> pages)
throws IOException {
for (PageIo cur : pages) {
cur.decrementTransactionCount();
if (!cur.isInTransaction()) {
owner.releaseFromTransaction(cur);
}
}
}
/**
* Starts a transaction. This can pages if all slots have been filled
* with full transactions, waiting for the synchronization thread to
* clean out slots.
*/
void start() throws IOException {
curTxn++;
if (curTxn == 1) {
synchronizeLogFromMemory();
curTxn = 0;
}
txn = new ArrayList();
}
/**
* Indicates the page is part of the transaction.
*/
void add(PageIo page) throws IOException {
page.incrementTransactionCount();
txn.add(page);
}
/**
* Commits the transaction to the log file.
*/
void commit() throws IOException {
LongPacker.packInt(oos, txn.size());
for (PageIo page : txn) {
page.writeExternal(oos, cipherIn);
}
sync();
// set clean flag to indicate pages have been written to log
setClean(txn);
// open a new ObjectOutputStream in order to store
// newer states of PageIo
// oos = new DataOutputStream(new BufferedOutputStream(fos));
}
/**
* Flushes and syncs
*/
private void sync() throws IOException {
oos.flush();
}
/**
* Shutdowns the transaction manager. Resynchronizes outstanding
* logs.
*/
void shutdown() throws IOException {
synchronizeLogFromMemory();
close();
}
/**
* Closes open files.
*/
private void close() throws IOException {
sync();
oos.close();
oos = null;
}
/**
* Force closing the file without synchronizing pending transaction data.
* Used for testing purposes only.
*/
void forceClose() throws IOException {
oos.close();
oos = null;
}
/**
* Use the disk-based transaction log to synchronize the data file.
* Outstanding memory logs are discarded because they are believed
* to be inconsistent.
*/
void synchronizeLogFromDisk() throws IOException {
close();
if (txn != null){
discardPages(txn);
txn = null;
}
recover();
open();
}
/**
* INNER CLASS.
* Comparator class for use by the tree set used to store the pages
* to write for this transaction. The PageIo objects are ordered by
* their page ids.
*/
private static final Comparator<PageIo> PAGE_IO_COMPARTOR = new Comparator<PageIo>() {
public int compare(PageIo page1, PageIo page2) {
if (page1.getPageId() == page2.getPageId()) {
return 0;
} else if (page1.getPageId() < page2.getPageId()) {
return -1;
} else {
return 1;
}
}
};
}

View File

@ -1,209 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOException;
import java.util.Arrays;
/**
* This class manages free physical rowid pages and provides methods to free and allocate physical rowids on a high
* level.
*/
final class PhysicalFreeRowIdManager {
/** maximal record size which can be hold. If record crosses multiple pages, it is trimmed before added to free list */
static final int MAX_REC_SIZE = Storage.PAGE_SIZE *2;
/** where data on root page starts, there are no extra data in page header */
static final int ROOT_HEADER_SIZE = Magic.PAGE_HEADER_SIZE;
/** page header size for slot page */
static final int SLOT_PAGE_HEADER_SIZE = Magic.PAGE_HEADER_SIZE + Magic.SZ_SHORT + Magic.SZ_SIX_BYTE_LONG;
/** number of recids on slot page */
static final int OFFSET_SLOT_PAGE_REC_COUNT = Magic.PAGE_HEADER_SIZE;
static final int SLOT_PAGE_REC_NUM = (Storage.PAGE_SIZE - SLOT_PAGE_HEADER_SIZE)/6;
/** pointer to next slo page in slot page header */
static final int OFFSET_SLOT_PAGE_NEXT = Magic.PAGE_HEADER_SIZE + Magic.SZ_SHORT;
/** number of size slots held in root page */
static final int MAX_RECIDS_PER_PAGE = (Storage.PAGE_SIZE -ROOT_HEADER_SIZE-6) / 6; //6 is size of page pointer
/** free records are grouped into slots by record size. Here is max diff in record size per group */
static final int ROOT_SLOT_SIZE = 1+MAX_REC_SIZE/ MAX_RECIDS_PER_PAGE;
protected final PageFile file;
protected final PageManager pageman;
/** list of free phys slots in current transaction. First two bytes are size, last 6 bytes are recid*/
private long[] inTrans = new long[8];
private int inTransSize = 0;
/**
* Creates a new instance using the indicated record file and page manager.
*/
PhysicalFreeRowIdManager(PageFile file, PageManager pageman) throws IOException {
this.file = file;
this.pageman = pageman;
}
long getFreeRecord(final int size) throws IOException {
if(size >= MAX_REC_SIZE) return 0;
final PageIo root = getRootPage();
final int rootPageOffset = sizeToRootOffset(size+ ROOT_SLOT_SIZE);
final long slotPageId = root.readSixByteLong(rootPageOffset);
if(slotPageId==0){
file.release(root);
return 0;
}
PageIo slotPage = file.get(slotPageId);
if(slotPage.readShort(Magic.PAGE_HEADER_O_MAGIC) != Magic.PAGE_MAGIC + Magic.FREEPHYSIDS_PAGE)
throw new InternalError();
short recidCount = slotPage.readShort(OFFSET_SLOT_PAGE_REC_COUNT);
if(recidCount<=0){
throw new InternalError();
}
final int offset = (recidCount-1) * 6 + SLOT_PAGE_HEADER_SIZE;
final long recid = slotPage.readSixByteLong(offset);
recidCount --;
if(recidCount>0){
//decrease counter and zero out old record
slotPage.writeSixByteLong(offset,0);
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT, recidCount);
file.release(root);
file.release(slotPage);
}else{
//release this page
long prevSlotPageId = slotPage.readSixByteLong(OFFSET_SLOT_PAGE_NEXT);
root.writeSixByteLong(rootPageOffset,prevSlotPageId);
file.release(root);
file.release(slotPage);
pageman.free(Magic.FREEPHYSIDS_PAGE,slotPageId);
}
return recid;
}
static final int sizeToRootOffset(int size) {
return ROOT_HEADER_SIZE + 6 * (size/ROOT_SLOT_SIZE);
}
/**
* Puts the indicated rowid on the free list, which awaits for commit
*/
void putFreeRecord(final long rowid, final int size) throws IOException {
//ensure capacity
if(inTransSize==inTrans.length){
inTrans = Arrays.copyOf(inTrans, inTrans.length * 2);
}
inTrans[inTransSize] = rowid + (((long)size)<<48);
inTransSize++;
}
public void commit() throws IOException {
if(inTransSize==0)
return;
Arrays.sort(inTrans,0,inTransSize-1);
//write all uncommited free records
final PageIo root = getRootPage();
PageIo slotPage = null;
for(int rowIdPos = 0; rowIdPos<inTransSize; rowIdPos++){
final int size = (int) (inTrans[rowIdPos] >>>48);
final long rowid = inTrans[rowIdPos] & 0x0000FFFFFFFFFFFFL;
final int rootPageOffset = sizeToRootOffset(size);
long slotPageId = root.readSixByteLong(rootPageOffset);
if(slotPageId == 0){
if(slotPage!=null) file.release(slotPage);
//create new page for this slot
slotPageId = pageman.allocate(Magic.FREEPHYSIDS_PAGE);
root.writeSixByteLong(rootPageOffset,slotPageId);
}
if(slotPage == null || slotPage.getPageId()!=slotPageId){
if(slotPage!=null) file.release(slotPage);
slotPage = file.get(slotPageId);
}
if(slotPage.readShort(Magic.PAGE_HEADER_O_MAGIC) != Magic.PAGE_MAGIC + Magic.FREEPHYSIDS_PAGE)
throw new InternalError();
short recidCount = slotPage.readShort(OFFSET_SLOT_PAGE_REC_COUNT);
if(recidCount== MAX_RECIDS_PER_PAGE){
file.release(slotPage);
//allocate new slot page and update links
final long newSlotPageId = pageman.allocate(Magic.FREEPHYSIDS_PAGE);
slotPage = file.get(newSlotPageId);
slotPage.writeSixByteLong(OFFSET_SLOT_PAGE_NEXT,slotPageId);
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT,(short)0);
recidCount = 0;
slotPageId = newSlotPageId;
root.writeSixByteLong(rootPageOffset,newSlotPageId);
}
//write new recid
slotPage.writeSixByteLong(recidCount * 6 + SLOT_PAGE_HEADER_SIZE,rowid);
//and increase count
recidCount++;
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT,recidCount);
}
if(slotPage!=null)
file.release(slotPage);
file.release(root);
clearFreeInTrans();
}
public void rollback() {
clearFreeInTrans();
}
private void clearFreeInTrans() {
if(inTrans.length>128)
inTrans = new long[8];
inTransSize = 0;
}
/** return free phys row page. If not found create it */
final PageIo getRootPage() throws IOException {
long pageId = pageman.getFirst(Magic.FREEPHYSIDS_ROOT_PAGE);
if(pageId == 0){
pageId = pageman.allocate(Magic.FREEPHYSIDS_ROOT_PAGE);
}
return file.get(pageId);
}
}

View File

@ -1,354 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOException;
import static org.apache.jdbm.Storage.*;
/**
* This class manages physical row ids, and their data.
*/
final class PhysicalRowIdManager {
// The file we're talking to and the associated page manager.
final private PageFile file;
final private PageManager pageman;
final PhysicalFreeRowIdManager freeman;
static final private short DATA_PER_PAGE = (short) (PAGE_SIZE - Magic.DATA_PAGE_O_DATA);
//caches offset after last allocation. So we dont have to iterate throw page every allocation
private long cachedLastAllocatedRecordPage = Long.MIN_VALUE;
private short cachedLastAllocatedRecordOffset = Short.MIN_VALUE;
/**
* Creates a new rowid manager using the indicated record file. and page manager.
*/
PhysicalRowIdManager(PageFile file, PageManager pageManager) throws IOException {
this.file = file;
this.pageman = pageManager;
this.freeman = new PhysicalFreeRowIdManager(file, pageManager);
}
/**
* Inserts a new record. Returns the new physical rowid.
*/
long insert(final byte[] data, final int start, final int length) throws IOException {
if (length < 1)
throw new IllegalArgumentException("Length is <1");
if (start < 0)
throw new IllegalArgumentException("negative start");
long retval = alloc(length);
write(retval, data, start, length);
return retval;
}
/**
* Updates an existing record. Returns the possibly changed physical rowid.
*/
long update(long rowid, final byte[] data, final int start, final int length) throws IOException {
// fetch the record header
PageIo page = file.get(rowid>>> Storage.PAGE_SIZE_SHIFT);
short head = (short) (rowid & Storage.OFFSET_MASK);
int availSize = RecordHeader.getAvailableSize(page, head);
if (length > availSize ||
//difference between free and available space can be only 254.
//if bigger, need to realocate and free page
availSize - length > RecordHeader.MAX_SIZE_SPACE
) {
// not enough space - we need to copy to a new rowid.
file.release(page);
free(rowid);
rowid = alloc(length);
} else {
file.release(page);
}
// 'nuff space, write it in and return the rowid.
write(rowid, data, start, length);
return rowid;
}
void fetch(final DataInputOutput out, final long rowid) throws IOException {
// fetch the record header
long current = rowid >>> Storage.PAGE_SIZE_SHIFT;
PageIo page = file.get(current);
final short head = (short) (rowid & Storage.OFFSET_MASK);
// allocate a return buffer
// byte[] retval = new byte[ head.getCurrentSize() ];
final int size = RecordHeader.getCurrentSize(page, head);
if (size == 0) {
file.release(current, false);
return;
}
// copy bytes in
int leftToRead = size;
short dataOffset = (short) ( head + RecordHeader.SIZE);
while (leftToRead > 0) {
// copy current page's data to return buffer
int toCopy = PAGE_SIZE - dataOffset;
if (leftToRead < toCopy) {
toCopy = leftToRead;
}
out.writeFromByteBuffer(page.getData(), dataOffset, toCopy);
// Go to the next page
leftToRead -= toCopy;
// out.flush();
file.release(page);
if (leftToRead > 0) {
current = pageman.getNext(current);
page = file.get(current);
dataOffset = Magic.DATA_PAGE_O_DATA;
}
}
// return retval;
}
/**
* Allocate a new rowid with the indicated size.
*/
private long alloc(int size) throws IOException {
size = RecordHeader.roundAvailableSize(size);
long retval = freeman.getFreeRecord(size);
if (retval == 0) {
retval = allocNew(size, pageman.getLast(Magic.USED_PAGE));
}
return retval;
}
/**
* Allocates a new rowid. The second parameter is there to allow for a recursive call - it indicates where the
* search should start.
*/
private long allocNew(int size, long start) throws IOException {
PageIo curPage;
if (start == 0 ||
//last page was completely filled?
cachedLastAllocatedRecordPage == start && cachedLastAllocatedRecordOffset == PAGE_SIZE
) {
// we need to create a new page.
start = pageman.allocate(Magic.USED_PAGE);
curPage = file.get(start);
curPage.dataPageSetFirst(Magic.DATA_PAGE_O_DATA);
cachedLastAllocatedRecordOffset = Magic.DATA_PAGE_O_DATA;
cachedLastAllocatedRecordPage = curPage.getPageId();
RecordHeader.setAvailableSize(curPage, Magic.DATA_PAGE_O_DATA, 0);
RecordHeader.setCurrentSize(curPage, Magic.DATA_PAGE_O_DATA, 0);
} else {
curPage = file.get(start);
}
// follow the rowids on this page to get to the last one. We don't
// fall off, because this is the last page, remember?
short pos = curPage.dataPageGetFirst();
if (pos == 0) {
// page is exactly filled by the last page of a record
file.release(curPage);
return allocNew(size, 0);
}
short hdr = pos;
if (cachedLastAllocatedRecordPage != curPage.getPageId() ) {
//position was not cached, have to find it again
int availSize = RecordHeader.getAvailableSize(curPage, hdr);
while (availSize != 0 && pos < PAGE_SIZE) {
pos += availSize + RecordHeader.SIZE;
if (pos == PAGE_SIZE) {
// Again, a filled page.
file.release(curPage);
return allocNew(size, 0);
}
hdr = pos;
availSize = RecordHeader.getAvailableSize(curPage, hdr);
}
} else {
hdr = cachedLastAllocatedRecordOffset;
pos = cachedLastAllocatedRecordOffset;
}
if (pos == RecordHeader.SIZE) { //TODO why is this here?
// the last record exactly filled the page. Restart forcing
// a new page.
file.release(curPage);
}
if(hdr>Storage.PAGE_SIZE - 16){
file.release(curPage);
//there is not enought space on current page, so force new page
return allocNew(size,0);
}
// we have the position, now tack on extra pages until we've got
// enough space.
long retval =(start << Storage.PAGE_SIZE_SHIFT) + (long) pos;
int freeHere = PAGE_SIZE - pos - RecordHeader.SIZE;
if (freeHere < size) {
// check whether the last page would have only a small bit left.
// if yes, increase the allocation. A small bit is a record
// header plus 16 bytes.
int lastSize = (size - freeHere) % DATA_PER_PAGE;
if (size <DATA_PER_PAGE && (DATA_PER_PAGE - lastSize) < (RecordHeader.SIZE + 16)) {
size += (DATA_PER_PAGE - lastSize);
size = RecordHeader.roundAvailableSize(size);
}
// write out the header now so we don't have to come back.
RecordHeader.setAvailableSize(curPage, hdr, size);
file.release(start, true);
int neededLeft = size - freeHere;
// Refactor these two pages!
while (neededLeft >= DATA_PER_PAGE) {
start = pageman.allocate(Magic.USED_PAGE);
curPage = file.get(start);
curPage.dataPageSetFirst((short) 0); // no rowids, just data
file.release(start, true);
neededLeft -= DATA_PER_PAGE;
}
if (neededLeft > 0) {
// done with whole chunks, allocate last fragment.
start = pageman.allocate(Magic.USED_PAGE);
curPage = file.get(start);
curPage.dataPageSetFirst((short) (Magic.DATA_PAGE_O_DATA + neededLeft));
file.release(start, true);
cachedLastAllocatedRecordOffset = (short) (Magic.DATA_PAGE_O_DATA + neededLeft);
cachedLastAllocatedRecordPage = curPage.getPageId();
}
} else {
// just update the current page. If there's less than 16 bytes
// left, we increase the allocation (16 bytes is an arbitrary
// number).
if (freeHere - size <= (16 + RecordHeader.SIZE)) {
size = freeHere;
}
RecordHeader.setAvailableSize(curPage, hdr, size);
file.release(start, true);
cachedLastAllocatedRecordOffset = (short) (hdr + RecordHeader.SIZE + size);
cachedLastAllocatedRecordPage = curPage.getPageId();
}
return retval;
}
void free(final long id) throws IOException {
// get the rowid, and write a zero current size into it.
final long curPageId = id >>> Storage.PAGE_SIZE_SHIFT;
final PageIo curPage = file.get(curPageId);
final short offset = (short) (id & Storage.OFFSET_MASK);
RecordHeader.setCurrentSize(curPage, offset, 0);
int size = RecordHeader.getAvailableSize(curPage, offset);
//trim size if spreads across multiple pages
if(offset + RecordHeader.SIZE + size >PAGE_SIZE + (PAGE_SIZE-Magic.DATA_PAGE_O_DATA)){
int numOfPagesToSkip = (size -
(Storage.PAGE_SIZE-(offset - RecordHeader.SIZE)) //minus data remaining on this page
)/(PAGE_SIZE-Magic.DATA_PAGE_O_DATA);
size = size - numOfPagesToSkip * (PAGE_SIZE-Magic.DATA_PAGE_O_DATA);
RecordHeader.setAvailableSize(curPage, offset,size);
//get next page
long nextPage = curPage.pageHeaderGetNext();
file.release(curPage);
//release pages
for(int i = 0;i<numOfPagesToSkip;i++){
PageIo page = file.get(nextPage);
long nextPage2 = page.pageHeaderGetNext();
file.release(page);
pageman.free(Magic.USED_PAGE,nextPage);
nextPage = nextPage2;
}
}else{
file.release(curPage);
}
// write the rowid to the free list
freeman.putFreeRecord(id, size);
}
/**
* Writes out data to a rowid. Assumes that any resizing has been done.
*/
private void write(final long rowid, final byte[] data,final int start, final int length) throws IOException {
long current = rowid >>> Storage.PAGE_SIZE_SHIFT;
PageIo page = file.get(current);
final short hdr = (short) (rowid & Storage.OFFSET_MASK);
RecordHeader.setCurrentSize(page, hdr, length);
if (length == 0) {
file.release(current, true);
return;
}
// copy bytes in
int offsetInBuffer = start;
int leftToWrite = length;
short dataOffset = (short) (hdr + RecordHeader.SIZE);
while (leftToWrite > 0) {
// copy current page's data to return buffer
int toCopy = PAGE_SIZE - dataOffset;
if (leftToWrite < toCopy) {
toCopy = leftToWrite;
}
page.writeByteArray(data, offsetInBuffer, dataOffset, toCopy);
// Go to the next page
leftToWrite -= toCopy;
offsetInBuffer += toCopy;
file.release(current, true);
if (leftToWrite > 0) {
current = pageman.getNext(current);
page = file.get(current);
dataOffset = Magic.DATA_PAGE_O_DATA;
}
}
}
void rollback() throws IOException {
cachedLastAllocatedRecordPage = Long.MIN_VALUE;
cachedLastAllocatedRecordOffset = Short.MIN_VALUE;
freeman.rollback();
}
void commit() throws IOException {
freeman.commit();
}
}

View File

@ -1,123 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
/**
* The data that comes at the start of a record of data. It stores
* both the current size and the avaliable size for the record - the latter
* can be bigger than the former, which allows the record to grow without
* needing to be moved and which allows the system to put small records
* in larger free spots.
* <p/>
* In JDBM 1.0 both values were stored as four-byte integers. This was very wastefull.
* Now available size is stored in two bytes, it is compressed, so maximal value is up to 120 MB (not sure with exact number)
* Current size is stored as two-byte-unsigned-short difference from Available Size.
*/
final class RecordHeader {
// offsets
private static final short O_CURRENTSIZE = 0; // int currentSize
private static final short O_AVAILABLESIZE = Magic.SZ_BYTE; // int availableSize
static final int MAX_RECORD_SIZE = 8355839;
static final int SIZE = O_AVAILABLESIZE + Magic.SZ_SHORT;
/**
* Maximal difference between current and available size,
* Maximal value is reserved for currentSize 0, so use -1
*/
static final int MAX_SIZE_SPACE = 255 - 1;
/**
* Returns the current size
*/
static int getCurrentSize(final PageIo page, final short pos) {
int s = page.readByte(pos + O_CURRENTSIZE) & 0xFF;
if (s == MAX_SIZE_SPACE + 1)
return 0;
return getAvailableSize(page, pos) - s;
}
/**
* Sets the current size
*/
static void setCurrentSize(final PageIo page, final short pos, int value) {
if (value == 0) {
page.writeByte(pos + O_CURRENTSIZE, (byte) (MAX_SIZE_SPACE + 1));
return;
}
int availSize = getAvailableSize(page, pos);
if (value < (availSize - MAX_SIZE_SPACE) || value > availSize)
throw new IllegalArgumentException("currentSize out of bounds, need to realocate " + value + " - " + availSize);
page.writeByte(pos + O_CURRENTSIZE, (byte) (availSize - value));
}
/**
* Returns the available size
*/
static int getAvailableSize(final PageIo page, final short pos) {
return deconvertAvailSize(page.readShort(pos + O_AVAILABLESIZE));
}
/**
* Sets the available size
*/
static void setAvailableSize(final PageIo page, final short pos, int value) {
if (value != roundAvailableSize(value))
throw new IllegalArgumentException("value is not rounded");
int oldCurrSize = getCurrentSize(page, pos);
page.writeShort(pos + O_AVAILABLESIZE, convertAvailSize(value));
setCurrentSize(page, pos, oldCurrSize);
}
static short convertAvailSize(final int recordSize) {
if (recordSize <= Short.MAX_VALUE)
return (short) recordSize;
else {
int shift = recordSize - Short.MAX_VALUE;
if (shift % MAX_SIZE_SPACE == 0)
shift = shift / MAX_SIZE_SPACE;
else
shift = 1 + shift / MAX_SIZE_SPACE;
shift = -shift;
return (short) (shift);
}
}
static int deconvertAvailSize(final short converted) {
if (converted >= 0)
return converted;
else {
int shifted = -converted;
shifted = shifted * MAX_SIZE_SPACE;
return Short.MAX_VALUE + shifted;
}
}
static int roundAvailableSize(int value) {
if (value > MAX_RECORD_SIZE)
new InternalError("Maximal record size (" + MAX_RECORD_SIZE + ") exceeded: " + value);
return deconvertAvailSize(convertAvailSize(value));
}
}

View File

@ -1,38 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.IOException;
/**
* An listener notifed when record is inserted, updated or removed.
* <p/>
* NOTE: this class was used in JDBM2 to support secondary indexes
* JDBM3 does not have a secondary indexes, so this class is not publicly exposed.
*
* @param <K> key type
* @param <V> value type
* @author Jan Kotek
*/
interface RecordListener<K, V> {
void recordInserted(K key, V value) throws IOException;
void recordUpdated(K key, V oldValue, V newValue) throws IOException;
void recordRemoved(K key, V value) throws IOException;
}

View File

@ -1,533 +0,0 @@
package org.apache.jdbm;
import org.apache.jdbm.Serialization.FastArrayList;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class stores information about serialized classes and fields.
*/
abstract class SerialClassInfo {
static final Serializer<ArrayList<ClassInfo>> serializer = new Serializer<ArrayList<ClassInfo>>() {
public void serialize(DataOutput out, ArrayList<ClassInfo> obj) throws IOException {
LongPacker.packInt(out, obj.size());
for (ClassInfo ci : obj) {
out.writeUTF(ci.getName());
out.writeBoolean(ci.isEnum);
out.writeBoolean(ci.isExternalizable);
if(ci.isExternalizable) continue; //no fields
LongPacker.packInt(out, ci.fields.size());
for (FieldInfo fi : ci.fields) {
out.writeUTF(fi.getName());
out.writeBoolean(fi.isPrimitive());
out.writeUTF(fi.getType());
}
}
}
public ArrayList<ClassInfo> deserialize(DataInput in) throws IOException, ClassNotFoundException {
int size = LongPacker.unpackInt(in);
ArrayList<ClassInfo> ret = new ArrayList<ClassInfo>(size);
for (int i = 0; i < size; i++) {
String className = in.readUTF();
boolean isEnum = in.readBoolean();
boolean isExternalizable = in.readBoolean();
int fieldsNum = isExternalizable? 0 : LongPacker.unpackInt(in);
FieldInfo[] fields = new FieldInfo[fieldsNum];
for (int j = 0; j < fieldsNum; j++) {
fields[j] = new FieldInfo(in.readUTF(), in.readBoolean(), in.readUTF(), Class.forName(className));
}
ret.add(new ClassInfo(className, fields,isEnum,isExternalizable));
}
return ret;
}
};
long serialClassInfoRecid;
public SerialClassInfo(DBAbstract db, long serialClassInfoRecid, ArrayList<ClassInfo> registered){
this.db = db;
this.serialClassInfoRecid = serialClassInfoRecid;
this.registered = registered;
}
/**
* Stores info about single class stored in JDBM.
* Roughly corresponds to 'java.io.ObjectStreamClass'
*/
static class ClassInfo {
private final String name;
private final List<FieldInfo> fields = new ArrayList<FieldInfo>();
private final Map<String, FieldInfo> name2fieldInfo = new HashMap<String, FieldInfo>();
private final Map<String, Integer> name2fieldId = new HashMap<String, Integer>();
private ObjectStreamField[] objectStreamFields;
final boolean isEnum;
final boolean isExternalizable;
ClassInfo(final String name, final FieldInfo[] fields, final boolean isEnum, final boolean isExternalizable) {
this.name = name;
this.isEnum = isEnum;
this.isExternalizable = isExternalizable;
for (FieldInfo f : fields) {
this.name2fieldId.put(f.getName(), this.fields.size());
this.fields.add(f);
this.name2fieldInfo.put(f.getName(), f);
}
}
public String getName() {
return name;
}
public FieldInfo[] getFields() {
return (FieldInfo[]) fields.toArray();
}
public FieldInfo getField(String name) {
return name2fieldInfo.get(name);
}
public int getFieldId(String name) {
Integer fieldId = name2fieldId.get(name);
if(fieldId != null)
return fieldId;
return -1;
}
public FieldInfo getField(int serialId) {
return fields.get(serialId);
}
public int addFieldInfo(FieldInfo field) {
name2fieldId.put(field.getName(), fields.size());
name2fieldInfo.put(field.getName(), field);
fields.add(field);
return fields.size() - 1;
}
public ObjectStreamField[] getObjectStreamFields() {
return objectStreamFields;
}
public void setObjectStreamFields(ObjectStreamField[] objectStreamFields) {
this.objectStreamFields = objectStreamFields;
}
}
/**
* Stores info about single field stored in JDBM.
* Roughly corresponds to 'java.io.ObjectFieldClass'
*/
static class FieldInfo {
private final String name;
private final boolean primitive;
private final String type;
private Class typeClass;
// Class containing this field
private final Class clazz;
private Object setter;
private int setterIndex;
private Object getter;
private int getterIndex;
public FieldInfo(String name, boolean primitive, String type, Class clazz) {
this.name = name;
this.primitive = primitive;
this.type = type;
this.clazz = clazz;
try {
this.typeClass = Class.forName(type);
} catch (ClassNotFoundException e) {
this.typeClass = null;
}
initSetter();
initGetter();
}
private void initSetter() {
// Set setter
String setterName = "set" + firstCharCap(name);
String fieldSetterName = clazz.getName() + "#" + setterName;
Class aClazz = clazz;
// iterate over class hierarchy, until root class
while (aClazz != Object.class) {
// check if there is getMethod
try {
Method m = aClazz.getMethod(setterName, typeClass);
if (m != null) {
setter = m;
return;
}
} catch (Exception e) {
// e.printStackTrace();
}
// no get method, access field directly
try {
Field f = aClazz.getDeclaredField(name);
// security manager may not be happy about this
if (!f.isAccessible())
f.setAccessible(true);
setter = f;
return;
} catch (Exception e) {
// e.printStackTrace();
}
// move to superclass
aClazz = aClazz.getSuperclass();
}
}
private void initGetter() {
// Set setter
String getterName = "get" + firstCharCap(name);
String fieldSetterName = clazz.getName() + "#" + getterName;
Class aClazz = clazz;
// iterate over class hierarchy, until root class
while (aClazz != Object.class) {
// check if there is getMethod
try {
Method m = aClazz.getMethod(getterName);
if (m != null) {
getter = m;
return;
}
} catch (Exception e) {
// e.printStackTrace();
}
// no get method, access field directly
try {
Field f = aClazz.getDeclaredField(name);
// security manager may not be happy about this
if (!f.isAccessible())
f.setAccessible(true);
getter = f;
return;
} catch (Exception e) {
// e.printStackTrace();
}
// move to superclass
aClazz = aClazz.getSuperclass();
}
}
public FieldInfo(ObjectStreamField sf, Class clazz) {
this(sf.getName(), sf.isPrimitive(), sf.getType().getName(), clazz);
}
public String getName() {
return name;
}
public boolean isPrimitive() {
return primitive;
}
public String getType() {
return type;
}
private String firstCharCap(String s) {
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
}
ArrayList<ClassInfo> registered;
Map<Class, Integer> class2classId = new HashMap<Class, Integer>();
Map<Integer, Class> classId2class = new HashMap<Integer, Class>();
final DBAbstract db;
public void registerClass(Class clazz) throws IOException {
if(clazz != Object.class)
assertClassSerializable(clazz);
if (containsClass(clazz))
return;
ObjectStreamField[] streamFields = getFields(clazz);
FieldInfo[] fields = new FieldInfo[streamFields.length];
for (int i = 0; i < fields.length; i++) {
ObjectStreamField sf = streamFields[i];
fields[i] = new FieldInfo(sf, clazz);
}
ClassInfo i = new ClassInfo(clazz.getName(), fields,clazz.isEnum(), Externalizable.class.isAssignableFrom(clazz));
class2classId.put(clazz, registered.size());
classId2class.put(registered.size(), clazz);
registered.add(i);
if (db != null)
db.update(serialClassInfoRecid, (Serialization) this, db.defaultSerializationSerializer);
}
private ObjectStreamField[] getFields(Class clazz) {
ObjectStreamField[] fields = null;
ClassInfo classInfo = null;
Integer classId = class2classId.get(clazz);
if (classId != null) {
classInfo = registered.get(classId);
fields = classInfo.getObjectStreamFields();
}
if (fields == null) {
ObjectStreamClass streamClass = ObjectStreamClass.lookup(clazz);
FastArrayList<ObjectStreamField> fieldsList = new FastArrayList<ObjectStreamField>();
while (streamClass != null) {
for (ObjectStreamField f : streamClass.getFields()) {
fieldsList.add(f);
}
clazz = clazz.getSuperclass();
streamClass = ObjectStreamClass.lookup(clazz);
}
fields = new ObjectStreamField[fieldsList
.size()];
for (int i = 0; i < fields.length; i++) {
fields[i] = fieldsList.get(i);
}
if(classInfo != null)
classInfo.setObjectStreamFields(fields);
}
return fields;
}
private void assertClassSerializable(Class clazz) throws NotSerializableException, InvalidClassException {
if(containsClass(clazz))
return;
if (!Serializable.class.isAssignableFrom(clazz))
throw new NotSerializableException(clazz.getName());
}
public Object getFieldValue(String fieldName, Object object) {
try {
registerClass(object.getClass());
} catch (IOException e) {
e.printStackTrace();
}
ClassInfo classInfo = registered.get(class2classId.get(object.getClass()));
return getFieldValue(classInfo.getField(fieldName), object);
}
public Object getFieldValue(FieldInfo fieldInfo, Object object) {
Object fieldAccessor = fieldInfo.getter;
try {
if (fieldAccessor instanceof Method) {
Method m = (Method) fieldAccessor;
return m.invoke(object);
} else {
Field f = (Field) fieldAccessor;
return f.get(object);
}
} catch (Exception e) {
}
throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.getName());
}
public void setFieldValue(String fieldName, Object object, Object value) {
try {
registerClass(object.getClass());
} catch (IOException e) {
e.printStackTrace();
}
ClassInfo classInfo = registered.get(class2classId.get(object.getClass()));
setFieldValue(classInfo.getField(fieldName), object, value);
}
public void setFieldValue(FieldInfo fieldInfo, Object object, Object value) {
Object fieldAccessor = fieldInfo.setter;
try {
if (fieldAccessor instanceof Method) {
Method m = (Method) fieldAccessor;
m.invoke(object, value);
} else {
Field f = (Field) fieldAccessor;
f.set(object, value);
}
return;
} catch (Throwable e) {
e.printStackTrace();
}
throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.getName());
}
public boolean containsClass(Class clazz) {
return (class2classId.get(clazz) != null);
}
public int getClassId(Class clazz) {
Integer classId = class2classId.get(clazz);
if(classId != null) {
return classId;
}
throw new Error("Class is not registered: " + clazz);
}
public void writeObject(DataOutput out, Object obj, FastArrayList objectStack) throws IOException {
registerClass(obj.getClass());
//write class header
int classId = getClassId(obj.getClass());
LongPacker.packInt(out, classId);
ClassInfo classInfo = registered.get(classId);
if(classInfo.isExternalizable){
Externalizable o = (Externalizable) obj;
DataInputOutput out2 = (DataInputOutput) out;
try{
out2.serializer = this;
out2.objectStack = objectStack;
o.writeExternal(out2);
}finally {
out2.serializer = null;
out2.objectStack = null;
}
return;
}
if(classInfo.isEnum) {
int ordinal = ((Enum)obj).ordinal();
LongPacker.packInt(out, ordinal);
}
ObjectStreamField[] fields = getFields(obj.getClass());
LongPacker.packInt(out, fields.length);
for (ObjectStreamField f : fields) {
//write field ID
int fieldId = classInfo.getFieldId(f.getName());
if (fieldId == -1) {
//field does not exists in class definition stored in db,
//propably new field was added so add field descriptor
fieldId = classInfo.addFieldInfo(new FieldInfo(f, obj.getClass()));
db.update(serialClassInfoRecid, (Serialization) this, db.defaultSerializationSerializer);
}
LongPacker.packInt(out, fieldId);
//and write value
Object fieldValue = getFieldValue(classInfo.getField(fieldId), obj);
serialize(out, fieldValue, objectStack);
}
}
public Object readObject(DataInput in, FastArrayList objectStack) throws IOException {
//read class header
try {
int classId = LongPacker.unpackInt(in);
ClassInfo classInfo = registered.get(classId);
// Class clazz = Class.forName(classInfo.getName());
Class clazz = classId2class.get(classId);
if(clazz == null)
clazz = Class.forName(classInfo.getName());
assertClassSerializable(clazz);
Object o;
if(classInfo.isEnum) {
int ordinal = LongPacker.unpackInt(in);
o = clazz.getEnumConstants()[ordinal];
}
else {
o = createInstance(clazz, Object.class);
}
objectStack.add(o);
if(classInfo.isExternalizable){
Externalizable oo = (Externalizable) o;
DataInputOutput in2 = (DataInputOutput) in;
try{
in2.serializer = this;
in2.objectStack = objectStack;
oo.readExternal(in2);
}finally {
in2.serializer = null;
in2.objectStack = null;
}
}else{
int fieldCount = LongPacker.unpackInt(in);
for (int i = 0; i < fieldCount; i++) {
int fieldId = LongPacker.unpackInt(in);
FieldInfo f = classInfo.getField(fieldId);
Object fieldValue = deserialize(in, objectStack);
setFieldValue(f, o, fieldValue);
}
}
return o;
} catch (Exception e) {
throw new Error("Could not instanciate class", e);
}
}
//TODO dependecy on nonpublic JVM API
static private sun.reflect.ReflectionFactory rf =
sun.reflect.ReflectionFactory.getReflectionFactory();
private static Map<Class, Constructor> class2constuctor = new HashMap<Class, Constructor>();
/**
* Little trick to create new instance without using constructor.
* Taken from http://www.javaspecialists.eu/archive/Issue175.html
*/
private static <T> T createInstance(Class<T> clazz, Class<? super T> parent) {
try {
Constructor intConstr = class2constuctor.get(clazz);
if (intConstr == null) {
Constructor objDef = parent.getDeclaredConstructor();
intConstr = rf.newConstructorForSerialization(
clazz, objDef);
class2constuctor.put(clazz, intConstr);
}
return clazz.cast(intConstr.newInstance());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException("Cannot create object", e);
}
}
protected abstract Object deserialize(DataInput in, FastArrayList objectStack) throws IOException, ClassNotFoundException;
protected abstract void serialize(DataOutput out, Object fieldValue, FastArrayList objectStack) throws IOException;
//
}

View File

@ -1,142 +0,0 @@
package org.apache.jdbm;
/**
* Header byte, is used at start of each record to indicate data type
* WARNING !!! values bellow must be unique !!!!!
*/
final class SerializationHeader {
final static int NULL = 0;
final static int NORMAL = 1;
final static int BOOLEAN_TRUE = 2;
final static int BOOLEAN_FALSE = 3;
final static int INTEGER_MINUS_1 = 4;
final static int INTEGER_0 = 5;
final static int INTEGER_1 = 6;
final static int INTEGER_2 = 7;
final static int INTEGER_3 = 8;
final static int INTEGER_4 = 9;
final static int INTEGER_5 = 10;
final static int INTEGER_6 = 11;
final static int INTEGER_7 = 12;
final static int INTEGER_8 = 13;
final static int INTEGER_255 = 14;
final static int INTEGER_PACK_NEG = 15;
final static int INTEGER_PACK = 16;
final static int LONG_MINUS_1 = 17;
final static int LONG_0 = 18;
final static int LONG_1 = 19;
final static int LONG_2 = 20;
final static int LONG_3 = 21;
final static int LONG_4 = 22;
final static int LONG_5 = 23;
final static int LONG_6 = 24;
final static int LONG_7 = 25;
final static int LONG_8 = 26;
final static int LONG_PACK_NEG = 27;
final static int LONG_PACK = 28;
final static int LONG_255 = 29;
final static int LONG_MINUS_MAX = 30;
final static int SHORT_MINUS_1 = 31;
final static int SHORT_0 = 32;
final static int SHORT_1 = 33;
final static int SHORT_255 = 34;
final static int SHORT_FULL = 35;
final static int BYTE_MINUS_1 = 36;
final static int BYTE_0 = 37;
final static int BYTE_1 = 38;
final static int BYTE_FULL = 39;
final static int CHAR = 40;
final static int FLOAT_MINUS_1 = 41;
final static int FLOAT_0 = 42;
final static int FLOAT_1 = 43;
final static int FLOAT_255 = 44;
final static int FLOAT_SHORT = 45;
final static int FLOAT_FULL = 46;
final static int DOUBLE_MINUS_1 = 47;
final static int DOUBLE_0 = 48;
final static int DOUBLE_1 = 49;
final static int DOUBLE_255 = 50;
final static int DOUBLE_SHORT = 51;
final static int DOUBLE_FULL = 52;
final static int DOUBLE_ARRAY = 53;
final static int BIGDECIMAL = 54;
final static int BIGINTEGER = 55;
final static int FLOAT_ARRAY = 56;
final static int INTEGER_MINUS_MAX = 57;
final static int SHORT_ARRAY = 58;
final static int BOOLEAN_ARRAY = 59;
final static int ARRAY_INT_B_255 = 60;
final static int ARRAY_INT_B_INT = 61;
final static int ARRAY_INT_S = 62;
final static int ARRAY_INT_I = 63;
final static int ARRAY_INT_PACKED = 64;
final static int ARRAY_LONG_B = 65;
final static int ARRAY_LONG_S = 66;
final static int ARRAY_LONG_I = 67;
final static int ARRAY_LONG_L = 68;
final static int ARRAY_LONG_PACKED = 69;
final static int CHAR_ARRAY = 70;
final static int ARRAY_BYTE_INT = 71;
final static int NOTUSED_ARRAY_OBJECT_255 = 72;
final static int ARRAY_OBJECT = 73;
//special cases for BTree values which stores references
final static int ARRAY_OBJECT_PACKED_LONG = 74;
final static int ARRAYLIST_PACKED_LONG = 75;
final static int STRING_EMPTY = 101;
final static int NOTUSED_STRING_255 = 102;
final static int STRING = 103;
final static int NOTUSED_ARRAYLIST_255 = 104;
final static int ARRAYLIST = 105;
final static int NOTUSED_TREEMAP_255 = 106;
final static int TREEMAP = 107;
final static int NOTUSED_HASHMAP_255 = 108;
final static int HASHMAP = 109;
final static int NOTUSED_LINKEDHASHMAP_255 = 110;
final static int LINKEDHASHMAP = 111;
final static int NOTUSED_TREESET_255 = 112;
final static int TREESET = 113;
final static int NOTUSED_HASHSET_255 = 114;
final static int HASHSET = 115;
final static int NOTUSED_LINKEDHASHSET_255 = 116;
final static int LINKEDHASHSET = 117;
final static int NOTUSED_LINKEDLIST_255 = 118;
final static int LINKEDLIST = 119;
final static int NOTUSED_VECTOR_255 = 120;
final static int VECTOR = 121;
final static int IDENTITYHASHMAP = 122;
final static int HASHTABLE = 123;
final static int LOCALE = 124;
final static int PROPERTIES = 125;
final static int CLASS = 126;
final static int DATE = 127;
final static int UUID = 128;
static final int JDBMLINKEDLIST = 159;
static final int HTREE = 160;
final static int BTREE = 161;
static final int BTREE_NODE_LEAF = 162;
static final int BTREE_NODE_NONLEAF = 163;
static final int HTREE_BUCKET = 164;
static final int HTREE_DIRECTORY = 165;
/**
* used for reference to already serialized object in object graph
*/
static final int OBJECT_STACK = 166;
static final int JAVA_SERIALIZATION = 172;
}

View File

@ -1,51 +0,0 @@
/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.io.*;
/**
* Interface used to provide a serialization mechanism other than a class' normal
* serialization.
*
* @author Alex Boisvert
*/
public interface Serializer<A> {
/**
* Serialize the content of an object into a byte array.
*
* @param out ObjectOutput to save object into
* @param obj Object to serialize
*/
public void serialize(DataOutput out, A obj)
throws IOException;
/**
* Deserialize the content of an object from a byte array.
*
* @param in to read serialized data from
* @return deserialized object
* @throws IOException
* @throws ClassNotFoundException
*/
public A deserialize(DataInput in)
throws IOException, ClassNotFoundException;
}

View File

@ -1,54 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.nio.ByteBuffer;
/**
*
*/
interface Storage {
/**
* Bite shift used to calculate page size.
* If you want to modify page size, do it here.
*
* 1<<9 = 512
* 1<<10 = 1024
* 1<<11 = 2048
* 1<<12 = 4096
*/
int PAGE_SIZE_SHIFT = 12;
/**
* the lenght of single page.
* <p>
*!!! DO NOT MODIFY THI DIRECTLY !!!
*/
int PAGE_SIZE = 1<< PAGE_SIZE_SHIFT;
/**
* use 'val & OFFSET_MASK' to quickly get offset within the page;
*/
long OFFSET_MASK = 0xFFFFFFFFFFFFFFFFL >>> (64-Storage.PAGE_SIZE_SHIFT);
void write(long pageNumber, ByteBuffer data) throws IOException;
ByteBuffer read(long pageNumber) throws IOException;
void forceClose() throws IOException;
boolean isReadonly();
DataInputStream readTransactionLog();
void deleteTransactionLog();
void sync() throws IOException;
DataOutputStream openTransactionLog() throws IOException;
void deleteAllFiles() throws IOException;
}

View File

@ -1,192 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.List;
import static org.apache.jdbm.StorageDiskMapped.*;
/**
* Storage which used files on disk to store data
*/
class StorageDisk implements Storage {
private ArrayList<RandomAccessFile> rafs = new ArrayList<RandomAccessFile>();
private ArrayList<RandomAccessFile> rafsTranslation = new ArrayList<RandomAccessFile>();
private String fileName;
private long lastPageNumber = Long.MIN_VALUE;
private boolean readonly;
private boolean lockingDisabled;
public StorageDisk(String fileName,boolean readonly, boolean lockingDisabled) throws IOException {
this.fileName = fileName;
this.readonly = readonly;
this.lockingDisabled = lockingDisabled;
//make sure first file can be opened
//lock it
try {
if(!readonly && !lockingDisabled)
getRaf(0).getChannel().tryLock();
} catch (IOException e) {
throw new IOException("Could not lock DB file: " + fileName, e);
} catch (OverlappingFileLockException e) {
throw new IOException("Could not lock DB file: " + fileName, e);
}
}
RandomAccessFile getRaf(long pageNumber) throws IOException {
int fileNumber = (int) (Math.abs(pageNumber)/PAGES_PER_FILE );
List<RandomAccessFile> c = pageNumber>=0 ? rafs : rafsTranslation;
//increase capacity of array lists if needed
for (int i = c.size(); i <= fileNumber; i++) {
c.add(null);
}
RandomAccessFile ret = c.get(fileNumber);
if (ret == null) {
String name = StorageDiskMapped.makeFileName(fileName, pageNumber, fileNumber);
ret = new RandomAccessFile(name, readonly?"r":"rw");
c.set(fileNumber, ret);
}
return ret;
}
public void write(long pageNumber, ByteBuffer data) throws IOException {
if (data.capacity() != PAGE_SIZE) throw new IllegalArgumentException();
long offset = pageNumber * PAGE_SIZE;
RandomAccessFile file = getRaf(pageNumber);
// if (lastPageNumber + 1 != pageNumber) //TODO cache position again, so seek is not necessary
file.seek(Math.abs(offset % (PAGES_PER_FILE* PAGE_SIZE)));
file.write(data.array());
lastPageNumber = pageNumber;
}
public ByteBuffer read(long pageNumber) throws IOException {
long offset = pageNumber * PAGE_SIZE;
ByteBuffer buffer = ByteBuffer.allocate(PAGE_SIZE);
RandomAccessFile file = getRaf(pageNumber);
// if (lastPageNumber + 1 != pageNumber) //TODO cache position again, so seek is not necessary
file.seek(Math.abs(offset % (PAGES_PER_FILE* PAGE_SIZE)));
int remaining = buffer.limit();
int pos = 0;
while (remaining > 0) {
int read = file.read(buffer.array(), pos, remaining);
if (read == -1) {
System.arraycopy(PageFile.CLEAN_DATA, 0, buffer.array(), pos, remaining);
break;
}
remaining -= read;
pos += read;
}
lastPageNumber = pageNumber;
return buffer;
}
static final String transaction_log_file_extension = ".t";
public DataOutputStream openTransactionLog() throws IOException {
String logName = fileName + transaction_log_file_extension;
final FileOutputStream fileOut = new FileOutputStream(logName);
return new DataOutputStream(new BufferedOutputStream(fileOut)) {
//default implementation of flush on FileOutputStream does nothing,
//so we use little workaround to make sure that data were really flushed
public void flush() throws IOException {
super.flush();
fileOut.flush();
fileOut.getFD().sync();
}
};
}
public void deleteAllFiles() {
deleteTransactionLog();
StorageDiskMapped.deleteFiles(fileName);
}
/**
* Synchronizes the file.
*/
public void sync() throws IOException {
for (RandomAccessFile file : rafs)
if (file != null)
file.getFD().sync();
for (RandomAccessFile file : rafsTranslation)
if (file != null)
file.getFD().sync();
}
public void forceClose() throws IOException {
for (RandomAccessFile f : rafs) {
if (f != null)
f.close();
}
rafs = null;
for (RandomAccessFile f : rafsTranslation) {
if (f != null)
f.close();
}
rafsTranslation = null;
}
public DataInputStream readTransactionLog() {
File logFile = new File(fileName + transaction_log_file_extension);
if (!logFile.exists())
return null;
if (logFile.length() == 0) {
logFile.delete();
return null;
}
DataInputStream ois = null;
try {
ois = new DataInputStream(new BufferedInputStream(new FileInputStream(logFile)));
} catch (FileNotFoundException e) {
//file should exists, we check for its presents just a miliseconds yearlier, anyway move on
return null;
}
try {
if (ois.readShort() != Magic.LOGFILE_HEADER)
throw new Error("Bad magic on log file");
} catch (IOException e) {
// corrupted/empty logfile
logFile.delete();
return null;
}
return ois;
}
public void deleteTransactionLog() {
File logFile = new File(fileName + transaction_log_file_extension);
if (logFile.exists())
logFile.delete();
}
public boolean isReadonly() {
return false;
}
}

View File

@ -1,259 +0,0 @@
package org.apache.jdbm;
import sun.misc.Cleaner;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
/**
* Disk storage which uses mapped buffers
*/
class StorageDiskMapped implements Storage {
static final String IDR = ".i";
static final String DBR = ".d";
/**
* Maximal number of pages in single file.
* Calculated so that each file will have 1 GB
*/
final static long PAGES_PER_FILE = (1024*1024*1024)>>>Storage.PAGE_SIZE_SHIFT;
private ArrayList<FileChannel> channels = new ArrayList<FileChannel>();
private ArrayList<FileChannel> channelsTranslation = new ArrayList<FileChannel>();
private IdentityHashMap<FileChannel, MappedByteBuffer> buffers = new IdentityHashMap<FileChannel, MappedByteBuffer>();
private String fileName;
private boolean transactionsDisabled;
private boolean readonly;
private boolean lockingDisabled;
public StorageDiskMapped(String fileName, boolean readonly, boolean transactionsDisabled, boolean lockingDisabled) throws IOException {
this.fileName = fileName;
this.transactionsDisabled = transactionsDisabled;
this.readonly = readonly;
this.lockingDisabled = lockingDisabled;
//make sure first file can be opened
//lock it
try {
if(!lockingDisabled)
getChannel(0).lock();
} catch (IOException e) {
throw new IOException("Could not lock DB file: " + fileName, e);
} catch (OverlappingFileLockException e) {
throw new IOException("Could not lock DB file: " + fileName, e);
}
}
private FileChannel getChannel(long pageNumber) throws IOException {
int fileNumber = (int) (Math.abs(pageNumber)/PAGES_PER_FILE );
List<FileChannel> c = pageNumber>=0 ? channels : channelsTranslation;
//increase capacity of array lists if needed
for (int i = c.size(); i <= fileNumber; i++) {
c.add(null);
}
FileChannel ret = c.get(fileNumber);
if (ret == null) {
String name = makeFileName(fileName, pageNumber, fileNumber);
ret = new RandomAccessFile(name, "rw").getChannel();
c.set(fileNumber, ret);
buffers.put(ret, ret.map(FileChannel.MapMode.READ_WRITE, 0, ret.size()));
}
return ret;
}
static String makeFileName(String fileName, long pageNumber, int fileNumber) {
return fileName + (pageNumber>=0 ? DBR : IDR) + "." + fileNumber;
}
public void write(long pageNumber, ByteBuffer data) throws IOException {
if(transactionsDisabled && data.isDirect()){
//if transactions are disabled and this buffer is direct,
//changes written into buffer are directly reflected in file.
//so there is no need to write buffer second time
return;
}
FileChannel f = getChannel(pageNumber);
int offsetInFile = (int) ((Math.abs(pageNumber) % PAGES_PER_FILE)* PAGE_SIZE);
MappedByteBuffer b = buffers.get(f);
if( b.limit()<=offsetInFile){
//remapping buffer for each newly added page would be slow,
//so allocate new size in chunks
int increment = Math.min(PAGE_SIZE * 1024,offsetInFile/10);
increment -= increment% PAGE_SIZE;
long newFileSize = offsetInFile+ PAGE_SIZE + increment;
newFileSize = Math.min(PAGES_PER_FILE * PAGE_SIZE, newFileSize);
//expand file size
f.position(newFileSize - 1);
f.write(ByteBuffer.allocate(1));
//unmap old buffer
unmapBuffer(b);
//remap buffer
b = f.map(FileChannel.MapMode.READ_WRITE, 0,newFileSize);
buffers.put(f, b);
}
//write into buffer
b.position(offsetInFile);
data.rewind();
b.put(data);
}
private void unmapBuffer(MappedByteBuffer b) {
if(b!=null){
Cleaner cleaner = ((sun.nio.ch.DirectBuffer) b).cleaner();
if(cleaner!=null)
cleaner.clean();
}
}
public ByteBuffer read(long pageNumber) throws IOException {
FileChannel f = getChannel(pageNumber);
int offsetInFile = (int) ((Math.abs(pageNumber) % PAGES_PER_FILE)* PAGE_SIZE);
MappedByteBuffer b = buffers.get(f);
if(b == null){ //not mapped yet
b = f.map(FileChannel.MapMode.READ_WRITE, 0, f.size());
}
//check buffers size
if(b.limit()<=offsetInFile){
//file is smaller, return empty data
return ByteBuffer.wrap(PageFile.CLEAN_DATA).asReadOnlyBuffer();
}
b.position(offsetInFile);
ByteBuffer ret = b.slice();
ret.limit(PAGE_SIZE);
if(!transactionsDisabled||readonly){
// changes written into buffer will be directly written into file
// so we need to protect buffer from modifications
ret = ret.asReadOnlyBuffer();
}
return ret;
}
public void forceClose() throws IOException {
for(FileChannel f: channels){
if(f==null) continue;
f.close();
unmapBuffer(buffers.get(f));
}
for(FileChannel f: channelsTranslation){
if(f==null) continue;
f.close();
unmapBuffer(buffers.get(f));
}
channels = null;
channelsTranslation = null;
buffers = null;
}
public void sync() throws IOException {
for(MappedByteBuffer b: buffers.values()){
b.force();
}
}
public DataOutputStream openTransactionLog() throws IOException {
String logName = fileName + StorageDisk.transaction_log_file_extension;
final FileOutputStream fileOut = new FileOutputStream(logName);
return new DataOutputStream(new BufferedOutputStream(fileOut)) {
//default implementation of flush on FileOutputStream does nothing,
//so we use little workaround to make sure that data were really flushed
public void flush() throws IOException {
super.flush();
fileOut.flush();
fileOut.getFD().sync();
}
};
}
public void deleteAllFiles() throws IOException {
deleteTransactionLog();
deleteFiles(fileName);
}
static void deleteFiles(String fileName) {
for(int i = 0; true; i++){
String name = makeFileName(fileName,+1, i);
File f =new File(name);
boolean exists = f.exists();
if(exists && !f.delete()) f.deleteOnExit();
if(!exists) break;
}
for(int i = 0; true; i++){
String name = makeFileName(fileName,-1, i);
File f =new File(name);
boolean exists = f.exists();
if(exists && !f.delete()) f.deleteOnExit();
if(!exists) break;
}
}
public DataInputStream readTransactionLog() {
File logFile = new File(fileName + StorageDisk.transaction_log_file_extension);
if (!logFile.exists())
return null;
if (logFile.length() == 0) {
logFile.delete();
return null;
}
DataInputStream ois = null;
try {
ois = new DataInputStream(new BufferedInputStream(new FileInputStream(logFile)));
} catch (FileNotFoundException e) {
//file should exists, we check for its presents just a miliseconds yearlier, anyway move on
return null;
}
try {
if (ois.readShort() != Magic.LOGFILE_HEADER)
throw new Error("Bad magic on log file");
} catch (IOException e) {
// corrupted/empty logfile
logFile.delete();
return null;
}
return ois;
}
public void deleteTransactionLog() {
File logFile = new File(fileName + StorageDisk.transaction_log_file_extension);
if (logFile.exists())
logFile.delete();
}
public boolean isReadonly() {
return readonly;
}
}

View File

@ -1,96 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.nio.ByteBuffer;
/**
* Storage which keeps all data in memory.
* Data are lost after storage is closed.
*/
class StorageMemory implements Storage {
private LongHashMap<byte[]> pages = new LongHashMap<byte[]>();
private boolean transactionsDisabled;
StorageMemory(boolean transactionsDisabled){
this.transactionsDisabled = transactionsDisabled;
}
public ByteBuffer read(long pageNumber) throws IOException {
byte[] data = pages.get(pageNumber);
if (data == null) {
//out of bounds, so just return empty data
return ByteBuffer.wrap(PageFile.CLEAN_DATA).asReadOnlyBuffer();
}else{
ByteBuffer b = ByteBuffer.wrap(data);
if(!transactionsDisabled)
return b.asReadOnlyBuffer();
else
return b;
}
}
public void write(long pageNumber, ByteBuffer data) throws IOException {
if (data.capacity() != PAGE_SIZE) throw new IllegalArgumentException();
byte[] b = pages.get(pageNumber);
if(transactionsDisabled && data.hasArray() && data.array() == b){
//already putted directly into array
return;
}
if(b == null)
b = new byte[PAGE_SIZE];
data.position(0);
data.get(b,0, PAGE_SIZE);
pages.put(pageNumber,b);
}
public void sync() throws IOException {
}
public void forceClose() throws IOException {
pages = null;
}
private ByteArrayOutputStream transLog;
public DataInputStream readTransactionLog() {
if (transLog == null)
return null;
DataInputStream ret = new DataInputStream(
new ByteArrayInputStream(transLog.toByteArray()));
//read stream header
try {
ret.readShort();
} catch (IOException e) {
throw new IOError(e);
}
return ret;
}
public void deleteTransactionLog() {
transLog = null;
}
public DataOutputStream openTransactionLog() throws IOException {
if (transLog == null)
transLog = new ByteArrayOutputStream();
return new DataOutputStream(transLog);
}
public void deleteAllFiles() throws IOException {
}
public boolean isReadonly() {
return false;
}
}

View File

@ -1,71 +0,0 @@
package org.apache.jdbm;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* A read-only storage which reads data from compressed zip archive.
* <p/>
* To improve performance with compressed archives
* each page is stored in separate file (zip archive entry).
*/
class StorageZip implements Storage {
private String zip;
private String zip2;
private ZipFile z;
StorageZip(String zipFile) throws IOException {
zip = zipFile;
z = new ZipFile(zip);
zip2 = "db";
}
public void write(long pageNumber, ByteBuffer data) throws IOException {
throw new UnsupportedOperationException("readonly");
}
public ByteBuffer read(long pageNumber) throws IOException {
ByteBuffer data = ByteBuffer.allocate(PAGE_SIZE);
ZipEntry e = z.getEntry(zip2 + pageNumber);
if(e == null)
return ByteBuffer.wrap(PageFile.CLEAN_DATA).asReadOnlyBuffer();
InputStream i = z.getInputStream(e);
new DataInputStream(i).readFully(data.array());
i.close();
return data;
}
public void forceClose() throws IOException {
z.close();
z = null;
}
public DataInputStream readTransactionLog() {
throw new UnsupportedOperationException("readonly");
}
public void deleteTransactionLog() {
throw new UnsupportedOperationException("readonly");
}
public void sync() throws IOException {
throw new UnsupportedOperationException("readonly");
}
public DataOutputStream openTransactionLog() throws IOException {
throw new UnsupportedOperationException("readonly");
}
public void deleteAllFiles() throws IOException {
}
public boolean isReadonly() {
return true;
}
}

View File

@ -1,110 +0,0 @@
package org.apache.jdbm;
import javax.crypto.Cipher;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOError;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Comparator;
/**
* Various utilities used in JDBM
*/
@SuppressWarnings("rawtypes")
class Utils {
/**
* empty string is used as dummy value to represent null values in HashSet and TreeSet
*/
static final String EMPTY_STRING = "";
public static byte[] encrypt(Cipher cipherIn, ByteBuffer b) {
if(cipherIn==null && b.hasArray())
return b.array();
byte[] bb = new byte[Storage.PAGE_SIZE];
b.rewind();
b.get(bb,0,Storage.PAGE_SIZE);
return encrypt(cipherIn,bb);
}
public static byte[] encrypt(Cipher cipherIn, byte[] b) {
if (cipherIn == null)
return b;
try {
return cipherIn.doFinal(b);
} catch (Exception e) {
throw new IOError(e);
}
}
/**
* Compares comparables. Default comparator for most of java types
*/
static final Comparator COMPARABLE_COMPARATOR = new Comparator<Comparable>() {
@SuppressWarnings("unchecked")
public int compare(Comparable o1, Comparable o2) {
return o1 == null && o2 != null ? -1 : (o1 != null && o2 == null ? 1 : o1.compareTo(o2));
}
};
static String formatSpaceUsage(long size) {
if (size < 1e4)
return size + "B";
else if (size < 1e7)
return "" + Math.round(1D * size / 1024D) + "KB";
else if (size < 1e10)
return "" + Math.round(1D * size / 1e6) + "MB";
else
return "" + Math.round(1D * size / 1e9) + "GB";
}
static boolean allZeros(byte[] b) {
for (int i = 0; i < b.length; i++) {
if (b[i] != 0) return false;
}
return true;
}
@SuppressWarnings("unchecked")
static <E> E max(E e1, E e2, Comparator comp){
if(e1 == null) return e2;
if(e2 == null) return e1;
if(comp == null)
comp = COMPARABLE_COMPARATOR;
return comp.compare(e1,e2)<0 ? e2:e1;
}
@SuppressWarnings("unchecked")
static <E> E min(E e1, E e2, Comparator comp){
if(e1 == null) return e2;
if(e2 == null) return e1;
if(comp == null)
comp = COMPARABLE_COMPARATOR;
return comp.compare(e1,e2)>0 ? e2:e1;
}
static final Serializer<Object> NULL_SERIALIZER = new Serializer<Object>() {
public void serialize(DataOutput out, Object obj) throws IOException {
out.writeByte(11);
}
public Object deserialize(DataInput in) throws IOException, ClassNotFoundException {
in.readByte();
return null;
}
};
}

View File

@ -1,200 +0,0 @@
<html>
<body>
<h1>WARNING incomplete and missleading doc!!!</h1>
<p>This package contains public API and introduction</p>
<h2>JDBM intro</h2>
Key-Value databases have got a lot of attention recently, but their history is much older. GDBM (predecessor of JDBM)
started
in 1970 and was called 'pre rational' database. JDBM is under development since 2000. Version 1.0 was in production
since 2005 with only a few bugs reported. Version 2.0 adds some features on top of JDBM (most importantly <code>java.util.Map</code>
views)
<p/>
JDBM 2.0 goal is to provide simple and fast persistence. It is very simple to use, it has minimal overhead and
standalone
JAR takes only 130KB. It is excelent choice for Swing application or Android phone. JDBM also handles huge datasets well
and can be used for data processing (author is using it to process astronomical data).
The source code is not complicated; it is well readabable and can also be used for teaching.
On the other hand, it does not have some important features (concurrent scalability, multiple transaction, annotations,
clustering...), which is the reason why it is so simple and small. For example, multiple transaction would introduce a
new dimension of problems, such as concurrent updates, optimistic/pesimistic record locking, etc.
JDBM does not try to replicate Valdemort, HBase or other more advanced Key Value databases.
<p/>
<h2>JDBM2 is </h2>
<p/><b>Not a SQL database</b><br/>
JDBM2 is more low level. With this comes great power (speed, resource usage, no ORM)
but also big responsibility. You are responsible for data integrity, partioning, typing etc...
Excelent embedded SQL database is <a href="http://www.h2database.com">H2</a> (in fact it is faster than JDBM2 in many
cases).
<p/><b>Not an Object database</b><br/>
The fact that JDBM2 uses serialization may give you a false sense of security. It does not
magically split a huge object graph into smaller pieces, nor does it handle duplicates.
With JDBM you may easily end up with single instance being persisted in several copies over a datastore.
An object database would do this magic for you as it traverses object graph references and
makes sure there are no duplicates in a datastore. Have look at
<a href="http://www.neodatis.org/">NeoDatis</a> or <a href="http://www.db4o.com/">DB4o</a>
<p/><b>Not at enterprise level</b><br/>
JDBM2 codebase is propably very good and without bugs, but it is a community project. You may easily endup without
support. For something more enterprisey have a look at
<a href="http://www.oracle.com/database/berkeley-db/je/index.html ">Berkley DB Java Edition</a> from Oracle. BDB has
more
features, it is more robust, it has better documentation, bigger overhead and comes with a pricetag.
<p/><b>Not distributed</b><br/>
Key Value databases are associated with distributed stores, map reduce etc. JDBM is not distributed, it runs on single
computer only.
It does not even have a network interface and can not act as a server.
You would be propably looking for <a href="http://project-voldemort.com/">Valdemort</a>.
<h2>JDBM2 overview</h2>
JDBM2 has some helpfull features to make it easier to use. It also brings it closer to SQL and helps with data
integrity checks and data queries.
<p/><b>Low level node store</b><br/>
This is Key-Value database in its literal mean. Key is a record identifier number (recid) which points to a location in
file.
Since recid is a physical pointer, new key values must be assgned by store (wherever the free space is found).
Value can be any object, serializable to a byte[] array. Page store also provides transaction and cache.
<p/><b>Named objects</b><br/>
Number as an identifier is not very practical. So there is a table that translates Strings to recid. This is recommended
approach for persisting singletons.
<p/><b>Primary maps</b><br/>
{@link jdbm.PrimaryTreeMap} and {@link jdbm.PrimaryHashMap} implements <code>java.util.map</code> interface
from Java Collections. But they use node store for persistence. So you can create HashMap with bilions of items and
worry only about the commits.
<p/><b>Secondary maps</b><br/>
Secondary maps (indexes) provide side information and associations for the primary map. For example, if there is a
Person class persisted in the primary map,
the secondary maps can provide fast lookup by name, address, age... The secondary maps are 'views' to the primary map
and are readonly.
They are updated by the primary map automatically.
<p/><b>Cache</b><br/>
JDBM has object instance cache. This reduces the serialization time and disk IO. By default JDBM uses SoftReference
cache. If JVM have
less then 50MB heap space available, MRU (Most Recently Used) fixed size cache is used instead.
<p/><b>Transactions</b><br/>
JDBM provides transactions with commit and rollback. The transaction mechanism is safe and tested (in usage for the last
5 years). JDBM allows only
single concurrent transactions and there are no problems with concurrent updates and locking.
<h1>10 things to keep in mind</h1>
<ul>
<li>Uncommited data are stored in memory, and if you get <code>OutOfMemoryException</code> you have to make commits
more
frequently.
<li>Keys and values are stored as part of the index nodes. They are instanciated each time the index is searched.
If you have larger values (>512 bytes), these may hurt performance and cause <code>OutOfMemoryException</code>
<li>If you run into performance problems, use the profiler rather then asking for it over the internet.
<li>JDBM caches returned object instances. If you modify an object (like set new name on a person),
next time RecordManager may return the object with this modification.
<li>Iteration over Maps is not guaranteed if there are changes
(for example adding a new entry while iterating). There is no fail fast policy yet.
So all iterations over Maps should be synchronized on RecordManager.
<li>More memory means better performance; use <code>-Xmx000m</code> generously. JDBM has good SoftReference cache.
<li>SoftReference cache may be blocking some memory for other tasks. The memory is released automatically, but it
may take longer then you expect.
Consider clearing the cache manually with <code>RecordManager.clearCache()</code> before starting a new type
of task.
<li>It is safe not to close the db before exiting, but if you that there will be a long cleanup upon the next start.
<li>JDBM may have problem reclaiming free space after many records are delete/updated. You may want to run
<code>RecordManager.defrag()</code> from time to time.
<li>A Key-Value db does not support N-M relations easily. It takes a lot of care to handle them correctly.
</ul>
<dl>
</dl>
<!-- $Id: package.html,v 1.1 2001/05/19 16:01:33 boisvert Exp $ -->
<html>
<body>
<p>Core classes for managing persistent objects and processing transactions.</p>
<h1>Memory allocation</h1>
This document describes the memory allocation structures and
algorithms used by jdbm. It is based on a thread in the
jdbm-developers mailing list.
<p/>
<ul>
<li> A block is a fixed length of bytes. Also known as a node.
<li> A row is a variable length of bytes. Also known as a record.
<li> A slot is a fixed length entry in a given block/node.
<li> A node list is a linked list of pages. The head and tail of each
node list is maintained in the file header.
</ul>
Jdbm knows about a few node lists which are pre-defined in Magic,
e.g., Magic.USED_PAGE. The FREE, USED, TRANSLATION, FREELOGIDS, and
FREEPHYSIDS node lists are used by the jdbm memory allocation policy
and are described below.
<p/>
The translation list consists of a bunch of slots that can be
available (free) or unavailable (allocated). If a slot is available,
then it contains junk data (it is available to map the logical row id
associated with that slot to some physical row id). If it is
unavailable, then it contains the block id and offset of the header of
a valid (non-deleted) record. "Available" for the translation list
is marked by a zero block id for that slot.
<p/>
The free logical row id list consists of a set of pages that contain
slots. Each slot is either available (free) or unavailable
(allocated). If it is unavailable, then it contains a reference to
the location of the available slot in the translation list. If it is
available, then it contains junk data. "Available" slots are marked by
a zero block id. A count is maintained of the #of available slots
(free row ids) on the node.
<p/>
As you free a logical row id, you change it's slot in the translation
list from unavailable to available, and then *add* entries to the free
logical row list. Adding entries to the free logical row list is done
by finding an available slot in the free logical row list and
replacing the junk data in that slot with the location of the now
available slot in the translation list. A count is maintained of the
#of available slots (free row ids) on the node.
<p/>
Whew... now we've freed a logical row id. But what about the physical
row id?
<p/>
Well, the free physical row id list consists of a set of pages that
contain slots. Each slot is either available (free) or unavailable
(allocated). If it is unavailable, then it contains a reference to
the location of the newly freed row's header in the data node. If it
is available, then it contains junk data. "Available" slots are
marked by a zero block id. A count is maintained of the #of available
slots (free row ids) on the node. (Sound familiar?)
<p/>
As you free a physical row id, you change it's header in the data node
from inuse to free (by zeroing the size field of the record header),
and then *add* an entry to the free physical row list. Adding entries
to the free physical row list consists of finding an available slot,
and replacing the junk data in that slot with the location of the
newly freed row's header in the data node.
<p/>
The translation list is used for translating in-use logical row ids
to in-use physical row ids. When a physical row id is freed, it is
removed from the translation list and added to the free physical row
id list.
<p/>
This allows a complete decoupling of the logical row id from the
physical row id, which makes it super easy to do some of the fiddling
I'm talking about the coallescing and splitting records.
<p/>
If you want to get a list of the free records, just enumerate the
unavailable entries in the free physical row id list. You don't even
need to look up the record header because the length of the record is
also stored in the free physical row id list. As you enumerate the
list, be sure to not include slots that are available (in the current
incarnation of jdbm, I believe the available length is set to 0 to
indicate available - we'll be changing that some time soon here, I'm
sure).
<p/>
</body>
</html>
</body>
</html>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,19 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: SWT widget
Bundle-SymbolicName: com.minres.scviewer.database.ui.swt
Bundle-Version: 1.1.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.eclipse.swt;bundle-version="3.103.1",
com.minres.scviewer.database;bundle-version="1.0.0",
com.google.guava;bundle-version="15.0.0",
org.eclipse.jface,
org.eclipse.equinox.registry,
com.minres.scviewer.database.ui,
org.eclipse.core.runtime,
org.eclipse.osgi
Export-Package: com.minres.scviewer.database.swt
Bundle-ClassPath: .
Bundle-ActivationPolicy: lazy
Bundle-Activator: com.minres.scviewer.database.swt.DatabaseUiPlugin

View File

@ -1,26 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt;
import org.eclipse.swt.widgets.Composite;
import com.minres.scviewer.database.swt.internal.WaveformViewer;
import com.minres.scviewer.database.ui.IWaveformViewer;
import com.minres.scviewer.database.ui.IWaveformViewerFactory;
public class WaveformViewerFactory implements IWaveformViewerFactory {
@Override
public IWaveformViewer createPanel(Composite parent) {
return new WaveformViewer(parent);
}
}

View File

@ -1,178 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt.internal;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.ITxRelation;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.ui.WaveformColors;
public class ArrowPainter implements IPainter {
private final int xCtrlOffset = 50;
private final int yCtrlOffset = 30;
private WaveformCanvas waveCanvas;
private ITx tx;
private List<LinkEntry> iRect;
private List<LinkEntry> oRect;
private Rectangle txRectangle;
private RelationType highlightType;
long scaleFactor;
boolean deferredUpdate;
public ArrowPainter(WaveformCanvas waveCanvas, RelationType relationType) {
this.waveCanvas = waveCanvas;
highlightType=relationType;
setTx(null);
}
public RelationType getHighlightType() {
return highlightType;
}
public void setHighlightType(RelationType highlightType) {
this.highlightType = highlightType;
}
public ITx getTx() {
return tx;
}
public void setTx(ITx newTx) {
this.tx = newTx;
iRect = new LinkedList<>();
oRect = new LinkedList<>();
scaleFactor = waveCanvas.getScaleFactor();
if (tx != null) {
calculateGeometries();
}
}
protected void calculateGeometries() {
deferredUpdate = false;
ITxStream<?> stream = tx.getStream();
IWaveformPainter painter = waveCanvas.wave2painterMap.get(stream);
if (painter == null) { // stream has been added but painter not yet
// created
deferredUpdate = true;
return;
}
int laneHeight = painter.getHeight() / stream.getMaxConcurrency();
txRectangle = new Rectangle((int) (tx.getBeginTime() / scaleFactor),
waveCanvas.rulerHeight + painter.getVerticalOffset() + laneHeight * tx.getConcurrencyIndex(),
(int) ((tx.getEndTime() - tx.getBeginTime()) / scaleFactor), laneHeight);
deriveGeom(tx.getIncomingRelations(), iRect, false);
deriveGeom(tx.getOutgoingRelations(), oRect, true);
}
protected void deriveGeom(Collection<ITxRelation> relations, List<LinkEntry> res, boolean useTarget) {
for (ITxRelation iTxRelation : relations) {
ITx otherTx = useTarget ? iTxRelation.getTarget() : iTxRelation.getSource();
if (waveCanvas.wave2painterMap.containsKey(otherTx.getStream())) {
ITxStream<?> stream = otherTx.getStream();
IWaveformPainter painter = waveCanvas.wave2painterMap.get(stream);
int laneHeight = painter.getHeight() / stream.getMaxConcurrency();
Rectangle bb = new Rectangle((int) (otherTx.getBeginTime() / scaleFactor),
waveCanvas.rulerHeight + painter.getVerticalOffset()
+ laneHeight * otherTx.getConcurrencyIndex(),
(int) ((otherTx.getEndTime() - otherTx.getBeginTime()) / scaleFactor), laneHeight);
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
}
}
}
@Override
public void paintArea(GC gc, Rectangle area) {
Color fgColor = waveCanvas.colors[WaveformColors.REL_ARROW.ordinal()];
Color highliteColor = waveCanvas.colors[WaveformColors.REL_ARROW_HIGHLITE.ordinal()];
if (deferredUpdate || (tx != null && waveCanvas.getScaleFactor() != scaleFactor)) {
scaleFactor = waveCanvas.getScaleFactor();
calculateGeometries();
}
for (LinkEntry entry : iRect) {
Point target = drawPath(gc, highlightType.equals(entry.relationType) ? highliteColor : fgColor,
entry.rectangle, txRectangle);
drawArrow(gc, target);
}
for (LinkEntry entry : oRect) {
Point target = drawPath(gc, highlightType.equals(entry.relationType) ? highliteColor : fgColor, txRectangle,
entry.rectangle);
drawArrow(gc, target);
}
}
protected void drawArrow(GC gc, Point target) {
gc.drawLine(target.x - 8, target.y - 5, target.x, target.y);
gc.drawLine(target.x - 8, target.y + 5, target.x, target.y);
}
protected Point drawPath(GC gc, Color fgColor, Rectangle srcRectangle, Rectangle tgtRectangle) {
Point point1 = new Point(0, srcRectangle.y + srcRectangle.height / 2);
Point point2 = new Point(0, tgtRectangle.y + tgtRectangle.height / 2);
point1.x = srcRectangle.x;
point2.x = tgtRectangle.x;
if (point2.x > point1.x + srcRectangle.width)
point1.x += srcRectangle.width;
if (point1.x > point2.x + tgtRectangle.width)
point2.x += tgtRectangle.width;
Path path = new Path(Display.getCurrent());
path.moveTo(point1.x, point1.y);
if (point1.y == point2.y) {
Point center = new Point((point1.x + point2.x) / 2, point1.y - yCtrlOffset);
path.cubicTo(point1.x + xCtrlOffset, point1.y, center.x - xCtrlOffset, center.y, center.x, center.y);
path.cubicTo(center.x + xCtrlOffset, center.y, point2.x - xCtrlOffset, point2.y, point2.x, point2.y);
} else
path.cubicTo(point1.x + xCtrlOffset, point1.y, point2.x - xCtrlOffset, point2.y, point2.x, point2.y);
gc.setAntialias(SWT.ON);
gc.setForeground(fgColor);
gc.drawPath(path);
path.dispose();
return point2;
}
class LinkEntry {
public Rectangle rectangle;
public RelationType relationType;
public LinkEntry(Rectangle rectangle, RelationType relationType) {
super();
this.rectangle = rectangle;
this.relationType = relationType;
}
}
}

View File

@ -1,75 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt.internal;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.wb.swt.SWTResourceManager;
public class RulerPainter implements IPainter {
protected WaveformCanvas waveCanvas;
static final int rulerTickMinorC = 10;
static final int rulerTickMajorC = 100;
public RulerPainter(WaveformCanvas waveCanvas) {
this.waveCanvas=waveCanvas;
}
@Override
public void paintArea(GC gc, Rectangle area) {
Color headerFgColor=waveCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
if(headerFgColor.isDisposed())
headerFgColor=SWTResourceManager.getColor(0,0,0);
Color headerBgColor = waveCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
if(headerBgColor.isDisposed())
headerBgColor=SWTResourceManager.getColor(255,255,255);
String unit=waveCanvas.getUnitStr();
int unitMultiplier=waveCanvas.getUnitMultiplier();
long scaleFactor=waveCanvas.getScaleFactor();
// long zoomLevel = waveCanvas.getZoomLevel();
long start=area.x*scaleFactor;
long end=start+area.width*scaleFactor;
long rulerTickMinor = rulerTickMinorC*scaleFactor;
long rulerTickMajor = rulerTickMajorC*scaleFactor;
int minorTickY = waveCanvas.rulerHeight-5;
int majorTickY = waveCanvas.rulerHeight-15;
int textY=waveCanvas.rulerHeight-20;
int baselineY=waveCanvas.rulerHeight - 1;
int bottom=waveCanvas.rulerHeight - 2;
long startMinorIncr = start;
long modulo = start % rulerTickMinor;
startMinorIncr+=rulerTickMinor-modulo;
gc.setBackground(waveCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
gc.fillRectangle(new Rectangle(area.x, area.y, area.width, waveCanvas.rulerHeight));
gc.setBackground(headerBgColor);
gc.fillRectangle(new Rectangle(area.x, area.y, area.width, baselineY));
gc.setForeground(headerFgColor);
gc.drawLine(area.x, area.y+bottom, area.x+area.width, area.y+bottom);
for (long tick = startMinorIncr; tick < end; tick += rulerTickMinor) {
int x0 = (int) (tick/scaleFactor);
if ((tick % rulerTickMajor) == 0) {
gc.drawText(Double.toString(tick/scaleFactor*unitMultiplier)+unit, x0, area.y+textY);
gc.drawLine(x0, area.y+majorTickY, x0,area.y+ bottom);
} else {
gc.drawLine(x0, area.y+minorTickY, x0, area.y+bottom);
}
}
}
}

View File

@ -1,264 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt.internal;
import java.util.Map.Entry;
import java.util.NavigableMap;
import javax.swing.JPanel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import com.minres.scviewer.database.ISignal;
import com.minres.scviewer.database.ISignalChange;
import com.minres.scviewer.database.ISignalChangeMulti;
import com.minres.scviewer.database.ISignalChangeSingle;
import com.minres.scviewer.database.ui.TrackEntry;
import com.minres.scviewer.database.ui.WaveformColors;
public class SignalPainter extends TrackPainter {
private class SignalChange {
long time;
ISignalChange value;
boolean fromMap;
public SignalChange(Entry<Long, ? extends ISignalChange> entry) {
time = entry.getKey();
value = entry.getValue();
fromMap = true;
}
public void set(Entry<Long, ? extends ISignalChange> entry, Long actTime) {
if (entry != null) {
time = entry.getKey();
value = entry.getValue();
fromMap = true;
} else {
time = actTime;
fromMap = false;
}
}
public void assign(SignalChange other) {
time = other.time;
value = other.value;
fromMap = other.fromMap;
}
}
/**
*
*/
private static final JPanel DUMMY_PANEL = new JPanel();
private final WaveformCanvas waveCanvas;
private ISignal<? extends ISignalChange> signal;
int yOffsetT;
int yOffsetM;
int yOffsetB;
int maxX;
public SignalPainter(WaveformCanvas txDisplay, boolean even, TrackEntry trackEntry) {
super(trackEntry, even);
this.waveCanvas = txDisplay;
this.signal = trackEntry.getSignal();
}
private int getXEnd(long time) {
long ltmp = time / this.waveCanvas.getScaleFactor();
return ltmp > maxX ? maxX : (int) ltmp;
}
public void paintArea(GC gc, Rectangle area) {
if (trackEntry.selected)
gc.setBackground(this.waveCanvas.colors[WaveformColors.TRACK_BG_HIGHLITE.ordinal()]);
else
gc.setBackground(this.waveCanvas.colors[even ? WaveformColors.TRACK_BG_EVEN.ordinal() : WaveformColors.TRACK_BG_ODD.ordinal()]);
gc.setFillRule(SWT.FILL_EVEN_ODD);
gc.fillRectangle(area);
long beginTime = area.x * this.waveCanvas.getScaleFactor();
long endTime = (area.x + area.width) * this.waveCanvas.getScaleFactor();
Entry<Long, ? extends ISignalChange> first = signal.getEvents().floorEntry(beginTime);
Entry<Long, ? extends ISignalChange> last = signal.getEvents().floorEntry(endTime);
if (first == null) {
if (last == null)
return;
first = signal.getEvents().firstEntry();
} else if (last == null) {
last = signal.getEvents().lastEntry();
}
gc.setForeground(this.waveCanvas.colors[WaveformColors.LINE.ordinal()]);
gc.setLineStyle(SWT.LINE_SOLID);
gc.setLineWidth(1);
NavigableMap<Long, ? extends ISignalChange> entries = signal.getEvents().subMap(first.getKey(), false, last.getKey(), true);
SignalChange left = new SignalChange(first);
SignalChange right = new SignalChange(entries.size() > 0 ? entries.firstEntry() : first);
SignalStencil stencil = left.value instanceof ISignalChangeSingle ? new SingleBitStencil() : new MultiBitStencil(gc);
maxX = area.x + area.width;
yOffsetT = this.waveCanvas.getTrackHeight() / 5 + area.y;
yOffsetM = this.waveCanvas.getTrackHeight() / 2 + area.y;
yOffsetB = 4 * this.waveCanvas.getTrackHeight() / 5 + area.y;
int xBegin = Math.max(area.x, (int) (left.time / this.waveCanvas.getScaleFactor()));
int xEnd = Math.max(area.x, getXEnd(right.time));
boolean multiple = false;
if (xEnd == xBegin) {
// this can trigger if
// a) left == right
// b) left to close to right
if (left.time == right.time) {
right.time = endTime;
} else {
multiple = true;
long eTime = (xBegin + 1) * this.waveCanvas.getScaleFactor();
right.set(entries.floorEntry(eTime), endTime);
right.time = eTime;
}
xEnd = getXEnd(right.time);
}
do {
stencil.draw(gc, area, left.value, right.value, xBegin, xEnd, multiple);
if (right.time >= endTime)
break;
left.assign(right);
xBegin = xEnd;
right.set(entries.higherEntry(left.time), endTime);
xEnd = getXEnd(right.time);
multiple = false;
if (xEnd == xBegin) {
multiple = true;
long eTime = (xBegin + 1) * this.waveCanvas.getScaleFactor();
right.set(entries.floorEntry(eTime), endTime);
xEnd = getXEnd(eTime);
}
} while (left.time < endTime);
}
private interface SignalStencil {
public void draw(GC gc, Rectangle area, ISignalChange left, ISignalChange right, int xBegin, int xEnd, boolean multiple);
}
private class MultiBitStencil implements SignalStencil {
private java.awt.Font tmpAwtFont;
private int height;
public MultiBitStencil(GC gc) {
FontData fd = gc.getFont().getFontData()[0];
height = gc.getDevice().getDPI().y * fd.getHeight() / 72;
tmpAwtFont = new java.awt.Font(fd.getName(), fd.getStyle(), height);
}
public void draw(GC gc, Rectangle area, ISignalChange left, ISignalChange right, int xBegin, int xEnd, boolean multiple) {
Color colorBorder = waveCanvas.colors[WaveformColors.SIGNAL0.ordinal()];
ISignalChangeMulti last = (ISignalChangeMulti) left;
if (last.getValue().toString().contains("X")) {
colorBorder = waveCanvas.colors[WaveformColors.SIGNALX.ordinal()];
} else if (last.getValue().toString().contains("Z")) {
colorBorder = waveCanvas.colors[WaveformColors.SIGNALZ.ordinal()];
}
int width = xEnd - xBegin;
if (width > 1) {
int[] points = {
xBegin, yOffsetM,
xBegin + 1, yOffsetT,
xEnd - 1, yOffsetT,
xEnd, yOffsetM,
xEnd - 1, yOffsetB,
xBegin + 1, yOffsetB
};
gc.setForeground(colorBorder);
gc.drawPolygon(points);
gc.setForeground(waveCanvas.colors[WaveformColors.SIGNAL_TEXT.ordinal()]);
String label = "h'" + last.getValue().toHexString();
Point bb = getBoxWidth(gc, label);
if (xBegin < area.x) {
xBegin = area.x;
width = xEnd - xBegin;
}
if (width > (bb.x+1)) {
Rectangle old = gc.getClipping();
gc.setClipping(xBegin + 3, yOffsetT, xEnd - xBegin - 5, yOffsetB - yOffsetT);
gc.drawText(label, xBegin + 3, yOffsetM - bb.y / 2 - 1);
gc.setClipping(old);
}
} else {
gc.setForeground(colorBorder);
gc.drawLine(xEnd, yOffsetT, xEnd, yOffsetB);
}
}
private Point getBoxWidth(GC gc, String label) {
return new Point(DUMMY_PANEL.getFontMetrics(tmpAwtFont).stringWidth(label), height);
}
}
private class SingleBitStencil implements SignalStencil {
public void draw(GC gc, Rectangle area, ISignalChange left, ISignalChange right, int xBegin, int xEnd, boolean multiple) {
if (multiple) {
gc.setForeground(waveCanvas.colors[WaveformColors.SIGNALU.ordinal()]);
gc.drawLine(xBegin, yOffsetT, xBegin, yOffsetB);
gc.drawLine(xEnd, yOffsetT, xEnd, yOffsetB);
} else {
Color color = waveCanvas.colors[WaveformColors.SIGNALX.ordinal()];
int yOffset = yOffsetM;
switch (((ISignalChangeSingle) left).getValue()) {
case '1':
color = waveCanvas.colors[WaveformColors.SIGNAL1.ordinal()];
yOffset = yOffsetT;
break;
case '0':
color = waveCanvas.colors[WaveformColors.SIGNAL0.ordinal()];
yOffset = yOffsetB;
break;
case 'Z':
color = waveCanvas.colors[WaveformColors.SIGNALZ.ordinal()];
break;
default:
}
gc.setForeground(color);
if (xEnd > maxX) {
gc.drawLine(xBegin, yOffset, maxX, yOffset);
} else {
gc.drawLine(xBegin, yOffset, xEnd, yOffset);
int yNext = yOffsetM;
switch (((ISignalChangeSingle) right).getValue()) {
case '1':
yNext = yOffsetT;
break;
case '0':
yNext = yOffsetB;
break;
default:
}
if (yOffset != yNext)
gc.drawLine(xEnd, yOffset, xEnd, yNext);
}
}
}
}
public ISignal<? extends ISignalChange> getSignal() {
return signal;
}
}

View File

@ -1,159 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt.internal;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeSet;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.ui.TrackEntry;
import com.minres.scviewer.database.ui.WaveformColors;
public class StreamPainter extends TrackPainter{
/**
*
*/
private final WaveformCanvas waveCanvas;
private ITxStream<? extends ITxEvent> stream;
private int txBase, txHeight;
private boolean even;
private TreeSet<ITx> seenTx;
public StreamPainter(WaveformCanvas waveCanvas, boolean even, TrackEntry trackEntry) {
super(trackEntry, even);
this.waveCanvas = waveCanvas;
this.stream=trackEntry.getStream();
this.seenTx=new TreeSet<ITx>();
}
@SuppressWarnings("unchecked")
public void paintArea(GC gc, Rectangle area) {
if(stream.getEvents().size()==0) return;
int trackHeight=trackEntry.height/stream.getMaxConcurrency();
txBase=trackHeight/5;
txHeight=trackHeight*3/5;
if(trackEntry.selected)
gc.setBackground(this.waveCanvas.colors[WaveformColors.TRACK_BG_HIGHLITE.ordinal()]);
else
gc.setBackground(this.waveCanvas.colors[even?WaveformColors.TRACK_BG_EVEN.ordinal():WaveformColors.TRACK_BG_ODD.ordinal()]);
gc.setFillRule(SWT.FILL_EVEN_ODD);
gc.fillRectangle(area);
Entry<Long, ?> firstTx=stream.getEvents().floorEntry(area.x*waveCanvas.getScaleFactor());
Entry<Long, ?> lastTx=stream.getEvents().ceilingEntry((area.x+area.width)*waveCanvas.getScaleFactor());
if(firstTx==null) firstTx = stream.getEvents().firstEntry();
if(lastTx==null) lastTx=stream.getEvents().lastEntry();
gc.setFillRule(SWT.FILL_EVEN_ODD);
gc.setLineStyle(SWT.LINE_SOLID);
gc.setLineWidth(1);
gc.setForeground(this.waveCanvas.colors[WaveformColors.LINE.ordinal()]);
for(int y1=area.y+trackHeight/2; y1<area.y+trackEntry.height; y1+=trackHeight)
gc.drawLine(area.x, y1, area.x+area.width, y1);
if(firstTx==lastTx)
for(ITxEvent txEvent:(Collection<? extends ITxEvent>)firstTx.getValue())
drawTx(gc, area, txEvent.getTransaction());
else{
seenTx.clear();
NavigableMap<Long,?> entries = stream.getEvents().subMap(firstTx.getKey(), true, lastTx.getKey(), true);
boolean highlighed=false;
gc.setForeground(this.waveCanvas.colors[WaveformColors.LINE.ordinal()]);
gc.setBackground(this.waveCanvas.colors[WaveformColors.TX_BG.ordinal()]);
for(Entry<Long, ?> entry: entries.entrySet())
for(ITxEvent txEvent:(Collection<? extends ITxEvent>)entry.getValue()){
if(txEvent.getType()==ITxEvent.Type.BEGIN)
seenTx.add(txEvent.getTransaction());
if(txEvent.getType()==ITxEvent.Type.END){
ITx tx = txEvent.getTransaction();
highlighed|=waveCanvas.currentSelection!=null && waveCanvas.currentSelection.equals(tx);
drawTx(gc, area, tx);
seenTx.remove(tx);
}
}
for(ITx tx:seenTx){
drawTx(gc, area, tx);
}
if(highlighed){
gc.setForeground(this.waveCanvas.colors[WaveformColors.LINE_HIGHLITE.ordinal()]);
gc.setBackground(this.waveCanvas.colors[WaveformColors.TX_BG_HIGHLITE.ordinal()]);
drawTx(gc, area, waveCanvas.currentSelection);
}
}
}
protected void drawTx(GC gc, Rectangle area, ITx tx) {
int offset = tx.getConcurrencyIndex()*this.waveCanvas.getTrackHeight();
Rectangle bb = new Rectangle(
(int)(tx.getBeginTime()/this.waveCanvas.getScaleFactor()), area.y+offset+txBase,
(int)((tx.getEndTime()-tx.getBeginTime())/this.waveCanvas.getScaleFactor()), txHeight);
if(bb.x+bb.width<area.x || bb.x>area.x+area.width) return;
if(bb.width==0){
gc.drawLine(bb.x, bb.y, bb.x, bb.y+bb.height);
} else if(bb.width<10){
gc.fillRectangle(bb);
gc.drawRectangle(bb);
} else {
gc.fillRoundRectangle(bb.x, bb.y, bb.width, bb.height, 5, 5);
gc.drawRoundRectangle(bb.x, bb.y, bb.width, bb.height, 5, 5);
}
}
public ITx getClicked(Point point) {
int lane=point.y/waveCanvas.getTrackHeight();
Entry<Long, List<ITxEvent>> firstTx=stream.getEvents().floorEntry(point.x*waveCanvas.getScaleFactor());
if(firstTx!=null){
do {
ITx tx = getTxFromEntry(lane, point.x, firstTx);
if(tx!=null) return tx;
firstTx=stream.getEvents().lowerEntry(firstTx.getKey());
}while(firstTx!=null);
}
return null;
}
public ITxStream<? extends ITxEvent> getStream() {
return stream;
}
public void setStream(ITxStream<? extends ITxEvent> stream) {
this.stream = stream;
}
protected ITx getTxFromEntry(int lane, int offset, Entry<Long, List<ITxEvent>> firstTx) {
long timePoint=offset*waveCanvas.getScaleFactor();
for(ITxEvent evt:firstTx.getValue()){
ITx tx=evt.getTransaction();
if(evt.getType()==ITxEvent.Type.BEGIN && tx.getConcurrencyIndex()==lane && tx.getBeginTime()<=timePoint && tx.getEndTime()>=timePoint){
return evt.getTransaction();
}
}
// now with some fuzziness
timePoint=(offset-5)*waveCanvas.getScaleFactor();
long timePointHigh=(offset+5)*waveCanvas.getScaleFactor();
for(ITxEvent evt:firstTx.getValue()){
ITx tx=evt.getTransaction();
if(evt.getType()==ITxEvent.Type.BEGIN && tx.getConcurrencyIndex()==lane && tx.getBeginTime()<=timePointHigh && tx.getEndTime()>=timePoint){
return evt.getTransaction();
}
}
return null;
}
}

View File

@ -1,514 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.swt.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.wb.swt.SWTResourceManager;
import com.google.common.collect.Lists;
import com.minres.scviewer.database.ITx;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformEvent;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.ui.IWaveformViewer;
import com.minres.scviewer.database.ui.WaveformColors;
public class WaveformCanvas extends Canvas {
Color[] colors = new Color[WaveformColors.values().length];
private int trackHeight = 50;
private long scaleFactor = 1000000L; // 1ns
String unit="ns";
private int level = 12;
public final static String[] unitString={"fs", "ps", "ns", "µs", "ms"};//, "s"};
public final static int[] unitMultiplier={1, 3, 10, 30, 100, 300};
private long maxTime;
protected Point origin; /* original size */
protected Transform transform;
protected int rulerHeight=40;
protected List<IPainter> painterList;
ITx currentSelection;
private List<SelectionAdapter> selectionListeners;
private RulerPainter rulerPainter;
private TrackAreaPainter trackAreaPainter;
private ArrowPainter arrowPainter;
private List<CursorPainter> cursorPainters;
HashMap<IWaveform<?>, IWaveformPainter> wave2painterMap;
/**
* Constructor for ScrollableCanvas.
*
* @param parent
* the parent of this control.
* @param style
* the style of this control.
*/
public WaveformCanvas(final Composite parent, int style) {
super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL | SWT.H_SCROLL);
addControlListener(new ControlAdapter() { /* resize listener. */
public void controlResized(ControlEvent event) {
syncScrollBars();
}
});
addPaintListener(new PaintListener() { /* paint listener. */
public void paintControl(final PaintEvent event) {
paint(event.gc);
}
});
painterList = new LinkedList<IPainter>();
origin = new Point(0, 0);
transform = new Transform(getDisplay());
selectionListeners = new LinkedList<>();
cursorPainters= new ArrayList<>();
wave2painterMap=new HashMap<>();
initScrollBars();
initColors(null);
// order is important: it is bottom to top
trackAreaPainter=new TrackAreaPainter(this);
painterList.add(trackAreaPainter);
rulerPainter=new RulerPainter(this);
painterList.add(rulerPainter);
arrowPainter=new ArrowPainter(this, IWaveformViewer.NEXT_PREV_IN_STREAM);
painterList.add(arrowPainter);
CursorPainter cp = new CursorPainter(this, scaleFactor * 10, cursorPainters.size()-1);
painterList.add(cp);
cursorPainters.add(cp);
CursorPainter marker = new CursorPainter(this, scaleFactor * 100, cursorPainters.size()-1);
painterList.add(marker);
cursorPainters.add(marker);
wave2painterMap=new HashMap<>();
}
public void addCursoPainter(CursorPainter cursorPainter){
painterList.add(cursorPainter);
cursorPainters.add(cursorPainter);
}
public void initColors(HashMap<WaveformColors, RGB> colourMap) {
Display d = getDisplay();
if (colourMap != null) {
for (WaveformColors c : WaveformColors.values()) {
if (colourMap.containsKey(c)) {
colors[c.ordinal()] = new Color(d, colourMap.get(c));
}
}
redraw();
} else {
colors[WaveformColors.LINE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED);
colors[WaveformColors.LINE_HIGHLITE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_CYAN);
colors[WaveformColors.TRACK_BG_EVEN.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_BLACK);
colors[WaveformColors.TRACK_BG_ODD.ordinal()] = SWTResourceManager.getColor(40, 40, 40);
colors[WaveformColors.TRACK_BG_HIGHLITE.ordinal()] = SWTResourceManager.getColor(40, 40, 80);
colors[WaveformColors.TX_BG.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GREEN);
colors[WaveformColors.TX_BG_HIGHLITE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GREEN);
colors[WaveformColors.TX_BORDER.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED);
colors[WaveformColors.SIGNAL0.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GREEN);
colors[WaveformColors.SIGNAL1.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GREEN);
colors[WaveformColors.SIGNALZ.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_YELLOW);
colors[WaveformColors.SIGNALX.ordinal()] = SWTResourceManager.getColor(255, 51, 51);
colors[WaveformColors.SIGNALU.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_YELLOW);
colors[WaveformColors.SIGNAL_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE);
colors[WaveformColors.CURSOR.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED);
colors[WaveformColors.CURSOR_DRAG.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GRAY);
colors[WaveformColors.CURSOR_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE);
colors[WaveformColors.MARKER.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GRAY);
colors[WaveformColors.MARKER_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE);
colors[WaveformColors.REL_ARROW.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_MAGENTA);
colors[WaveformColors.REL_ARROW_HIGHLITE.ordinal()] = SWTResourceManager.getColor(255, 128, 255);
}
}
public void setHighliteRelation(RelationType relationType){
if(arrowPainter!=null){
boolean redraw = arrowPainter.getHighlightType()!=relationType;
arrowPainter.setHighlightType(relationType);
if(redraw) redraw();
}
}
public Point getOrigin() {
return origin;
}
public void setOrigin(Point origin) {
setOrigin(origin.x, origin.y);
}
public void setOrigin(int x, int y) {
checkWidget();
ScrollBar hBar = getHorizontalBar();
hBar.setSelection(-x);
x = -hBar.getSelection();
ScrollBar vBar = getVerticalBar();
vBar.setSelection(-y);
y = -vBar.getSelection();
origin.x = x;
origin.y = y;
syncScrollBars();
}
public long getMaxTime() {
return maxTime;
}
public void setMaxTime(long maxTime) {
this.maxTime = maxTime;
syncScrollBars();
}
public int getTrackHeight() {
return trackHeight;
}
public void setTrackHeight(int trackHeight) {
this.trackHeight = trackHeight;
syncScrollBars();
}
public int getZoomLevel() {
return level;
}
public int getMaxZoomLevel(){
return unitMultiplier.length*unitString.length-1;
}
public void setZoomLevel(int level) {
long oldScaleFactor=scaleFactor;
if(level<unitMultiplier.length*unitString.length){
this.level = level;
this.scaleFactor = (long) Math.pow(10, level/2);
if(level%2==1) this.scaleFactor*=3;
ITx tx = arrowPainter.getTx();
arrowPainter.setTx(null);
/*
* xc = tc/oldScaleFactor
* xoffs = xc+origin.x
* xcn = tc/newScaleFactor
* t0n = (xcn-xoffs)*scaleFactor
*/
long tc=cursorPainters.get(0).getTime(); // cursor time
long xc=tc/oldScaleFactor; // cursor total x-offset
long xoffs=xc+origin.x; // cursor offset relative to left border
long xcn=tc/scaleFactor; // new total x-offset
long originX=xcn-xoffs;
if(originX>0)
origin.x=(int) -originX; // new cursor time offset relative to left border
else
origin.x=0;
syncScrollBars();
arrowPainter.setTx(tx);
redraw();
}
}
public long getScaleFactor() {
return scaleFactor;
}
public long getScaleFactorPow10() {
int scale = level/unitMultiplier.length;
double res = Math.pow(1000, scale);
return (long) res;
}
public String getUnitStr(){
return unitString[level/unitMultiplier.length];
}
public int getUnitMultiplier(){
return unitMultiplier[level%unitMultiplier.length];
}
public long getTimeForOffset(int xOffset){
return (xOffset-origin.x) * scaleFactor;
}
public void addPainter(IPainter painter) {
painterList.add(painter);
redraw();
}
public void removePainter(IPainter painter) {
painterList.remove(painter);
redraw();
}
public void clearAllWaveformPainter() {
trackAreaPainter.getTrackVerticalOffset().clear();
wave2painterMap.clear();
syncScrollBars();
}
public void addWaveformPainter(IWaveformPainter painter) {
trackAreaPainter.addTrackPainter(painter);
wave2painterMap.put(painter.getTrackEntry().waveform, painter);
syncScrollBars();
}
public List<CursorPainter> getCursorPainters() {
return cursorPainters;
}
/**
* Dispose the garbage here
*/
public void dispose() {
transform.dispose();
for (WaveformColors c : WaveformColors.values())
colors[c.ordinal()].dispose();
super.dispose();
}
/* Initalize the scrollbar and register listeners. */
private void initScrollBars() {
ScrollBar horizontal = getHorizontalBar();
horizontal.setEnabled(false);
horizontal.setVisible(true);
horizontal.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (painterList.size() == 0)
return;
setOrigin(-((ScrollBar) event.widget).getSelection(), origin.y);
}
});
ScrollBar vertical = getVerticalBar();
vertical.setEnabled(false);
vertical.setVisible(true);
vertical.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (painterList.size() == 0)
return;
setOrigin(origin.x, -((ScrollBar) event.widget).getSelection());
}
});
}
/**
* Synchronize the scrollbar with the image. If the transform is out of
* range, it will correct it. This function considers only following factors
* :<b> transform, image size, client area</b>.
*/
public void syncScrollBars() {
if (painterList.size() == 0) {
redraw();
return;
}
int height = trackAreaPainter.getHeight();
int width = (int) (maxTime / scaleFactor);
ScrollBar horizontal = getHorizontalBar();
horizontal.setIncrement((int) (getClientArea().width / 100));
horizontal.setPageIncrement(getClientArea().width);
int cw = getClientArea().width;
if (width > cw) { /* image is wider than client area */
horizontal.setMaximum(width);
horizontal.setEnabled(true);
if (((int) -origin.x) > horizontal.getMaximum() - cw)
origin.x = -horizontal.getMaximum() + cw;
} else { /* image is narrower than client area */
horizontal.setEnabled(false);
}
horizontal.setSelection(-origin.x);
horizontal.setThumb(cw);
ScrollBar vertical = getVerticalBar();
vertical.setIncrement((int) (getClientArea().height / 100));
vertical.setPageIncrement((int) (getClientArea().height));
int ch = getClientArea().height;
if (height > ch) { /* image is higher than client area */
vertical.setMaximum(height);
vertical.setEnabled(true);
if (((int) -origin.y) > vertical.getMaximum() - ch)
origin.y = -vertical.getMaximum() + ch;
} else { /* image is less higher than client area */
vertical.setMaximum((int) (ch));
vertical.setEnabled(false);
}
vertical.setSelection(-origin.y);
vertical.setThumb(ch);
redraw();
fireSelectionEvent();
}
/* Paint function */
private void paint(GC gc) {
Rectangle clientRect = getClientArea(); /* Canvas' painting area */
clientRect.x = -origin.x;
clientRect.y = -origin.y;
// reset the transform
transform.identity();
// shift the content
transform.translate(origin.x, origin.y);
gc.setTransform(transform);
gc.setClipping(clientRect);
if (painterList.size() > 0 ) {
for (IPainter painter : painterList)
painter.paintArea(gc, clientRect);
} else {
gc.fillRectangle(clientRect);
initScrollBars();
}
}
public List<Object> getClicked(Point point) {
LinkedList<Object> result=new LinkedList<>();
for (IPainter p : Lists.reverse(painterList)) {
if (p instanceof TrackAreaPainter) {
int y = point.y - origin.y;
int x = point.x - origin.x;
Entry<Integer, IWaveformPainter> entry = trackAreaPainter.getTrackVerticalOffset().floorEntry(y);
if (entry != null) {
if (entry.getValue() instanceof StreamPainter) {
ITx tx = ((StreamPainter) entry.getValue()).getClicked(new Point(x, y - entry.getKey()));
if(tx!=null)
result.add(tx);
}
result.add(entry.getValue().getTrackEntry());
}
} else if (p instanceof CursorPainter) {
if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/scaleFactor) < 2) {
result.add(p);
}
}
}
return result;
}
public List<Object> getEntriesAtPosition(IWaveform<? extends IWaveformEvent> iWaveform, int i) {
LinkedList<Object> result=new LinkedList<>();
int x = i - origin.x;
for(IPainter p: wave2painterMap.values()){
if (p instanceof StreamPainter && ((StreamPainter)p).getStream()==iWaveform) {
result.add(((StreamPainter) p).getClicked(new Point(x, trackHeight/2)));
}
}
return result;
}
public void setSelected(ITx currentSelection) {
this.currentSelection = currentSelection;
if (currentSelection != null)
reveal(currentSelection);
arrowPainter.setTx(currentSelection);
redraw();
}
public void reveal(ITx tx) {
int lower = (int) (tx.getBeginTime() / scaleFactor);
int higher = (int) (tx.getEndTime() / scaleFactor);
Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2;
size.y -= getHorizontalBar().getSize().y;
if (lower < -origin.x) {
setOrigin(-lower, origin.y);
} else if (higher > (size.x - origin.x)) {
setOrigin(size.x - higher, origin.y);
}
for (IWaveformPainter painter : wave2painterMap.values()) {
if (painter instanceof StreamPainter && ((StreamPainter) painter).getStream() == tx.getStream()) {
int top = painter.getVerticalOffset() + trackHeight * tx.getConcurrencyIndex();
int bottom = top + trackHeight;
if (top < -origin.y) {
setOrigin(origin.x, -(top-trackHeight));
} else if (bottom > (size.y - origin.y)) {
setOrigin(origin.x, size.y - bottom);
}
}
}
}
public void reveal(long time) {
int scaledTime = (int) (time / scaleFactor);
Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2;
size.y -= getHorizontalBar().getSize().y;
if (scaledTime < -origin.x) {
setOrigin(-scaledTime+10, origin.y);
} else if (scaledTime > (size.x - origin.x)) {
setOrigin(size.x - scaledTime-30, origin.y);
}
}
public int getRulerHeight() {
return rulerHeight;
}
public void setRulerHeight(int rulerHeight) {
this.rulerHeight = rulerHeight;
}
public void addSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.add(selectionAdapter);
}
public void removeSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.remove(selectionAdapter);
}
/**
*
*/
protected void fireSelectionEvent() {
Event e = new Event();
e.widget = this;
e.detail=SWT.SELECTED;
e.type=SWT.Selection;
SelectionEvent ev = new SelectionEvent(e);
ev.x = origin.x;
ev.y = origin.y;
for (SelectionAdapter a : selectionListeners) {
a.widgetSelected(ev);
}
}
}

View File

@ -1,7 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,10 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Database UI
Bundle-SymbolicName: com.minres.scviewer.database.ui
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: com.minres.scviewer.database.ui
Require-Bundle: com.minres.scviewer.database,
org.eclipse.jface

View File

@ -1,4 +0,0 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.

View File

@ -1,103 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.ui;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.List;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Control;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.RelationType;
public interface IWaveformViewer extends PropertyChangeListener, ISelectionProvider{
String CURSOR_PROPERTY = "cursor_time";
String MARKER_PROPERTY = "marker_time";
public static final RelationType NEXT_PREV_IN_STREAM = RelationType.create("Prev/Next in stream");
public void addSelectionChangedListener(ISelectionChangedListener listener);
public void removeSelectionChangedListener(ISelectionChangedListener listener);
public Control getControl();
public Control getNameControl();
public Control getValueControl();
public Control getWaveformControl();
public ISelection getSelection();
public void setSelection(ISelection selection);
public void setSelection(ISelection selection, boolean addIfNeeded);
public void moveSelection(GotoDirection direction);
public void moveSelection(GotoDirection direction, RelationType relationType);
public void moveCursor(GotoDirection direction);
public List<TrackEntry> getStreamList();
public TrackEntry getEntryForStream(IWaveform<?> source);
public void moveSelectedTrack(int i);
public void setHighliteRelation(RelationType relationType);
public long getMaxTime();
public void setMaxTime(long maxTime);
public void setZoomLevel(int scale);
public int getZoomLevel();
public void setCursorTime(long time);
public void setMarkerTime(long time, int index);
public long getCursorTime();
public int getSelectedMarkerId();
public long getMarkerTime(int index);
public void addPropertyChangeListener(PropertyChangeListener listener);
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener);
public void removePropertyChangeListener(PropertyChangeListener listener);
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);
public String getScaledTime(long time);
public String[] getZoomLevels();
public List<ICursor> getCursorList();
public void setColors(HashMap<WaveformColors, RGB> colourMap);
public long getBaselineTime();
public void setBaselineTime(Long scale);
}

View File

@ -1,60 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.ui;
import com.minres.scviewer.database.ISignal;
import com.minres.scviewer.database.ISignalChange;
import com.minres.scviewer.database.ITxEvent;
import com.minres.scviewer.database.ITxStream;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformEvent;
public class TrackEntry {
final public IWaveform<? extends IWaveformEvent> waveform;
public int vOffset;
public int height;
public boolean selected;
public TrackEntry(IWaveform<? extends IWaveformEvent> waveform) {
this.waveform = waveform;
vOffset=0;
height=0;
selected=false;
}
public boolean isStream(){
return waveform instanceof ITxStream<?>;
}
public ITxStream<? extends ITxEvent> getStream(){
return (ITxStream<?>) waveform;
}
public boolean isSignal(){
return waveform instanceof ISignal<?>;
}
public ISignal<? extends ISignalChange> getSignal(){
return (ISignal<?>) waveform;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof TrackEntry){
TrackEntry o = (TrackEntry) obj;
return waveform==o.waveform && vOffset==o.vOffset;
}
return false;
}
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,7 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,204 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import java.io.File;
import java.io.FileInputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Vector;
import com.minres.scviewer.database.BitVector;
import com.minres.scviewer.database.ISignal;
import com.minres.scviewer.database.ISignalChange;
import com.minres.scviewer.database.ISignalChangeMulti;
import com.minres.scviewer.database.ISignalChangeReal;
import com.minres.scviewer.database.ISignalChangeSingle;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformDb;
import com.minres.scviewer.database.IWaveformDbLoader;
import com.minres.scviewer.database.IWaveformEvent;
import com.minres.scviewer.database.InputFormatException;
import com.minres.scviewer.database.RelationType;
/**
* The Class VCDDb.
*/
public class VCDDbLoader implements IWaveformDbLoader, IVCDDatabaseBuilder {
/** The Constant TIME_RES. */
private static final Long TIME_RES = 1000L; // ps;
/** The db. */
private IWaveformDb db;
/** The module stack. */
private Stack<String> moduleStack;
/** The signals. */
private List<IWaveform<? extends IWaveformEvent>> signals;
/** The max time. */
private long maxTime;
/**
* Instantiates a new VCD db.
*/
public VCDDbLoader() {
}
/** The date bytes. */
private byte[] dateBytes = "$date".getBytes();
/* (non-Javadoc)
* @see com.minres.scviewer.database.ITrDb#load(java.io.File)
*/
@SuppressWarnings("unchecked")
@Override
public boolean load(IWaveformDb db, File file) throws Exception {
this.db=db;
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[dateBytes.length];
int read = fis.read(buffer, 0, dateBytes.length);
fis.close();
if (read == dateBytes.length)
for (int i = 0; i < dateBytes.length; i++)
if (buffer[i] != dateBytes[i])
return false;
signals = new Vector<IWaveform<? extends IWaveformEvent>>();
moduleStack= new Stack<String>();
boolean res = new VCDFileParser(false).load(new FileInputStream(file), this);
moduleStack=null;
if(!res) throw new InputFormatException();
// calculate max time of database
for(IWaveform<? extends IWaveformEvent> waveform:signals)
maxTime= Math.max(maxTime, ((ISignal<? extends ISignalChange>)waveform).getEvents().lastKey());
// extend signals to hav a last value set at max time
for(IWaveform<? extends IWaveformEvent> waveform:signals){
TreeMap<Long,? extends ISignalChange> events = ((VCDSignal<? extends ISignalChange>)waveform).values;
if(events.lastKey()<maxTime){
ISignalChange x = events.lastEntry().getValue();
if(x instanceof ISignalChangeSingle)
((VCDSignal<ISignalChangeSingle>)waveform).values.put(maxTime,
new VCDSignalChangeSingle(maxTime, ((ISignalChangeSingle)x).getValue()));
else
if(x instanceof ISignalChangeMulti)
((VCDSignal<ISignalChangeMulti>)waveform).values.put(maxTime,
new VCDSignalChangeMulti(maxTime, ((ISignalChangeMulti)x).getValue()));
}
}
return true;
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.ITrDb#getMaxTime()
*/
@Override
public Long getMaxTime() {
return maxTime;
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.ITrDb#getAllWaves()
*/
@Override
public List<IWaveform<? extends IWaveformEvent>> getAllWaves() {
return signals;
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#enterModule(java.lang.String)
*/
@Override
public void enterModule(String tokenString) {
if(moduleStack.isEmpty())
moduleStack.push(tokenString);
else
moduleStack.push(moduleStack.peek()+"."+tokenString);
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#exitModule()
*/
@Override
public void exitModule() {
if(!moduleStack.isEmpty()) moduleStack.pop();
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#newNet(java.lang.String, int, int)
*/
@SuppressWarnings("unchecked")
@Override
public Integer newNet(String netName, int i, int width) {
int id = signals.size();
VCDSignal<? extends IWaveformEvent> signal;
if(width==1){
signal = i<0 ? new VCDSignal<ISignalChangeSingle>(db, id, netName) :
new VCDSignal<ISignalChangeSingle>((VCDSignal<ISignalChangeSingle>)signals.get(i), id, netName);
} else {
signal = i<0 ? new VCDSignal<ISignalChangeMulti>(db, id, netName, width) :
new VCDSignal<ISignalChangeMulti>((VCDSignal<VCDSignalChangeMulti>)signals.get(i), id, netName);
};
signals.add(signal);
return id;
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#getNetWidth(int)
*/
@Override
public int getNetWidth(int intValue) {
VCDSignal<? extends IWaveformEvent> signal = (VCDSignal<? extends IWaveformEvent>) signals.get(intValue);
return signal.getWidth();
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#appendTransition(int, long, com.minres.scviewer.database.vcd.BitVector)
*/
@SuppressWarnings("unchecked")
@Override
public void appendTransition(int signalId, long currentTime, Double value) {
VCDSignal<? extends IWaveformEvent> signal = (VCDSignal<? extends IWaveformEvent>) signals.get(signalId);
Long time = currentTime*TIME_RES;
((VCDSignal<ISignalChangeReal>)signal).values.put(time, new VCDSignalChangeReal(time, value));
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.vcd.ITraceBuilder#appendTransition(int, long, com.minres.scviewer.database.vcd.BitVector)
*/
@SuppressWarnings("unchecked")
@Override
public void appendTransition(int signalId, long currentTime, BitVector decodedValues) {
VCDSignal<? extends IWaveformEvent> signal = (VCDSignal<? extends IWaveformEvent>) signals.get(signalId);
Long time = currentTime*TIME_RES;
if(signal.getWidth()==1){
((VCDSignal<ISignalChangeSingle>)signal).values.put(time, new VCDSignalChangeSingle(time, decodedValues.getValue()[0]));
} else {
((VCDSignal<VCDSignalChangeMulti>)signal).values.put(time, new VCDSignalChangeMulti(time, decodedValues));
}
}
/* (non-Javadoc)
* @see com.minres.scviewer.database.IWaveformDbLoader#getAllRelationTypes()
*/
@Override
public Collection<RelationType> getAllRelationTypes(){
return Collections.emptyList();
}
}

View File

@ -1,119 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.ISignal;
import com.minres.scviewer.database.ISignalChange;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformDb;
import com.minres.scviewer.database.IWaveformEvent;
public class VCDSignal<T extends ISignalChange> extends HierNode implements ISignal<T> {
private long id;
private String fullName;
private final String kind = "signal";
private final int width;
private IWaveformDb db;
TreeMap<Long, T> values;
public VCDSignal(IWaveformDb db, String name) {
this(db, 0, name, 1);
}
public VCDSignal(IWaveformDb db, int id, String name) {
this(db, id,name,1);
}
public VCDSignal(IWaveformDb db, int id, String name, int width) {
super(name);
this.db=db;
fullName=name;
this.id=id;
this.width=width;
this.values=new TreeMap<Long, T>();
}
@SuppressWarnings("unchecked")
public VCDSignal(IWaveform<? extends ISignalChange> other, int id, String name) {
super(name);
fullName=name;
this.id=id;
assert(other instanceof VCDSignal<?>);
this.width=((VCDSignal<? extends IWaveformEvent>)other).width;
this.values=((VCDSignal<T>)other).values;
this.db=other.getDb();
}
@Override
public String getFullName() {
return fullName;
}
public void setId(int id) {
this.id=id;
}
@Override
public Long getId() {
return id;
}
@Override
public String getKind() {
return kind;
}
public int getWidth() {
return width;
}
@Override
public IWaveformDb getDb() {
return db;
}
public void addSignalChange(T change){
values.put(change.getTime(), change);
}
@Override
public NavigableMap<Long, T> getEvents() {
return values;
}
@Override
public T getWaveformEventsAtTime(Long time) {
return values.get(time);
}
@Override
public T getWaveformEventsBeforeTime(Long time) {
return values.floorEntry(time).getValue();
}
@Override
public Boolean equals(IWaveform<? extends IWaveformEvent> other) {
return(other instanceof VCDSignal<?> && this.getId()==other.getId());
}
}

View File

@ -1,43 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import com.minres.scviewer.database.BitVector;
import com.minres.scviewer.database.ISignalChangeMulti;
import com.minres.scviewer.database.SignalChange;
public class VCDSignalChangeMulti extends SignalChange implements ISignalChangeMulti, Cloneable {
private BitVector value;
public VCDSignalChangeMulti(Long time) {
super(time);
}
public VCDSignalChangeMulti(Long time, BitVector decodedValues) {
super(time);
this.value=decodedValues;
}
public BitVector getValue() {
return value;
}
public void setValue(BitVector value) {
this.value = value;
}
@Override
public String toString() {
return value.toHexString()+"@"+getTime();
}
}

View File

@ -1,37 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import com.minres.scviewer.database.ISignalChangeReal;
import com.minres.scviewer.database.SignalChange;
public class VCDSignalChangeReal extends SignalChange implements ISignalChangeReal, Cloneable {
private double value;
public VCDSignalChangeReal(Long time, double value) {
super(time);
this.value=value;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
@Override
public String toString() {
return value+"@"+getTime();
}
}

View File

@ -1,37 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import com.minres.scviewer.database.ISignalChangeSingle;
import com.minres.scviewer.database.SignalChange;
public class VCDSignalChangeSingle extends SignalChange implements ISignalChangeSingle, Cloneable {
private char value;
public VCDSignalChangeSingle(Long time, char value) {
super(time);
this.value=value;
}
public char getValue() {
return value;
}
public void setValue(char value) {
this.value = value;
}
@Override
public String toString() {
return value+"@"+getTime();
}
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,8 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -1,13 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Waveform database
Bundle-SymbolicName: com.minres.scviewer.database
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: com.minres.scviewer.database
Bundle-ActivationPolicy: lazy
Service-Component: OSGI-INF/component.xml,OSGI-INF/component2.xml
Require-Bundle: org.eclipse.equinox.ds;bundle-version="1.4.200",
org.eclipse.equinox.util;bundle-version="1.0.500",
org.eclipse.osgi.services;bundle-version="3.4.0"

View File

@ -1,66 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database;
public class BitVector {
public static final char VALUE_X = 'X';
public static final char VALUE_Z = 'Z';
public static final char VALUE_1 = '1';
public static final char VALUE_0 = '0';
private final int width;
private char[] value;
public BitVector(int netWidth) {
this.width=netWidth;
value = new char[netWidth];
for(int i=0; i<netWidth; i++) value[i]='0';
}
public void setValue(int i, char value) {
this.value[i]=value;
}
public char[] getValue() {
return value;
}
public void setValue(char[] value) {
this.value = value;
}
public int getWidth() {
return width;
}
public String toString(){
return new String(value);
}
public String toHexString(){
int resWidth=(width-1)/4+1;
char[] res = new char[resWidth];
for(int i=resWidth-1; i>=0; i--){
int digit=0;
for(int j=3; j>=0; j--){
if(value[4*i+j]==VALUE_X ||value[4*i+j]==VALUE_Z ){
res[i]=VALUE_X;
}
if(value[4*i+j]==VALUE_1)
digit+=1<<(3-j);
res[i]=Character.forDigit(digit, 16); //((digit < 10) ? '0' + digit : 'a' + digit -10)
}
}
return new String(res);
}
}

View File

@ -1,28 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database;
public enum DataType {
BOOLEAN, // bool
ENUMERATION, // enum
INTEGER, // char, short, int, long, long long, sc_int, sc_bigint
UNSIGNED, // unsigned { char, short, int, long, long long }, sc_uint,
// sc_biguint
FLOATING_POINT_NUMBER, // float, double
BIT_VECTOR, // sc_bit, sc_bv
LOGIC_VECTOR, // sc_logic, sc_lv
FIXED_POINT_INTEGER, // sc_fixed
UNSIGNED_FIXED_POINT_INTEGER, // sc_ufixed
RECORD, // struct/class
POINTER, // T*
ARRAY, // T[N]
STRING // string, std::string
};

View File

@ -1,87 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;
public class HierNode implements IHierNode {
protected String name;
protected String parentName;
protected ArrayList<IHierNode> childs;
protected PropertyChangeSupport pcs;
public HierNode() {
childs = new ArrayList<IHierNode>();
pcs=new PropertyChangeSupport(this);
}
public HierNode(String name) {
this(name, "");
}
public HierNode(String name, String parentName) {
this();
this.name=name;
this.parentName=parentName;
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
@Override
public String getFullName() {
if(parentName!=null && parentName.length()>0)
return parentName+"."+name;
else
return name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name=name;
}
@Override
public void setParentName(String name) {
this.parentName=name;
}
@Override
public List<IHierNode> getChildNodes() {
return childs;
}
@Override
public int compareTo(IHierNode o) {
return name.compareTo(o.getName());
}
}

View File

@ -1,46 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database;
import java.beans.PropertyChangeListener;
import java.util.List;
public interface IHierNode extends Comparable<IHierNode>{
/**
* Attach a non-null PropertyChangeListener to this object.
*
* @param l
* a non-null PropertyChangeListener instance
* @throws IllegalArgumentException
* if the parameter is null
*/
public void addPropertyChangeListener(PropertyChangeListener l);
/**
* Remove a PropertyChangeListener from this component.
*
* @param l
* a PropertyChangeListener instance
*/
public void removePropertyChangeListener(PropertyChangeListener l) ;
public String getFullName();
public String getName();
public void setName(String name);
public void setParentName(String name);
public List<IHierNode> getChildNodes();
}

Some files were not shown because too many files have changed in this diff Show More