1- use alloc:: { boxed:: Box , sync:: Arc , vec, vec:: Vec } ;
2- use core:: mem;
3-
4- use hashbrown:: HashMap ;
5- use parking_lot:: Mutex ;
1+ use alloc:: vec;
62
73use java_class_proto:: { JavaFieldProto , JavaMethodProto } ;
8- use jvm:: { ClassInstance , ClassInstanceRef , Jvm , Result } ;
4+ use jvm:: { ClassInstanceRef , Jvm , Result } ;
95
106use crate :: { RuntimeClassProto , RuntimeContext , classes:: java:: lang:: Object } ;
117
12- // I'm too lazy to implement hashmap in java, so i'm leveraging rust hashmap here...
13- // We can't use java object as hashmap key as we need `await` to call `equals()`
14- type RustHashMap = Arc < Mutex < HashMap < i32 , Vec < ( Box < dyn ClassInstance > , Box < dyn ClassInstance > ) > > > > ;
8+ use super :: HashtableEntry ;
9+
10+ const DEFAULT_INITIAL_CAPACITY : i32 = 11 ;
11+ const DEFAULT_LOAD_FACTOR : f32 = 0.75 ;
1512
1613// class java.util.Hashtable
1714pub struct Hashtable ;
@@ -34,131 +31,207 @@ impl Hashtable {
3431 JavaMethodProto :: new( "get" , "(Ljava/lang/Object;)Ljava/lang/Object;" , Self :: get, Default :: default ( ) ) ,
3532 JavaMethodProto :: new( "remove" , "(Ljava/lang/Object;)Ljava/lang/Object;" , Self :: remove, Default :: default ( ) ) ,
3633 ] ,
37- fields : vec ! [ JavaFieldProto :: new( "raw" , "[B" , Default :: default ( ) ) ] ,
34+ fields : vec ! [
35+ JavaFieldProto :: new( "table" , "[Ljava/util/Hashtable$Entry;" , Default :: default ( ) ) ,
36+ JavaFieldProto :: new( "count" , "I" , Default :: default ( ) ) ,
37+ JavaFieldProto :: new( "threshold" , "I" , Default :: default ( ) ) ,
38+ ] ,
3839 access_flags : Default :: default ( ) ,
3940 }
4041 }
4142
4243 async fn init ( jvm : & Jvm , _: & mut RuntimeContext , mut this : ClassInstanceRef < Self > ) -> Result < ( ) > {
43- tracing:: debug!( "java.util.Hashtable::<init>({:?})" , & this ) ;
44+ tracing:: debug!( "java.util.Hashtable::<init>({this :?})" ) ;
4445
4546 let _: ( ) = jvm. invoke_special ( & this, "java/util/Dictionary" , "<init>" , "()V" , ( ) ) . await ?;
4647
47- let rust_hash_map: RustHashMap = Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ;
48- jvm. put_rust_object_field ( & mut this, "raw" , rust_hash_map) . await ?;
48+ let table = jvm. instantiate_array ( "Ljava/util/Hashtable$Entry;" , DEFAULT_INITIAL_CAPACITY as _ ) . await ?;
49+ jvm. put_field ( & mut this, "table" , "[Ljava/util/Hashtable$Entry;" , table) . await ?;
50+ jvm. put_field ( & mut this, "count" , "I" , 0 ) . await ?;
51+ jvm. put_field ( & mut this, "threshold" , "I" , ( DEFAULT_INITIAL_CAPACITY as f32 * DEFAULT_LOAD_FACTOR ) as i32 ) . await ?;
4952
5053 Ok ( ( ) )
5154 }
5255
53- // TODO we need to add synchronized
5456 async fn contains_key ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > , key : ClassInstanceRef < Object > ) -> Result < bool > {
55- tracing:: debug!( "java.util.Hashtable::containsKey({:?}, {:?})" , & this , & key ) ;
57+ tracing:: debug!( "java.util.Hashtable::containsKey({this :?}, {key :?})" ) ;
5658
57- let rust_hash_map = Self :: get_rust_hashmap ( jvm, & this) . await ?;
5859 let key_hash: i32 = jvm. invoke_virtual ( & key, "hashCode" , "()I" , ( ) ) . await ?;
59-
60- let vec = rust_hash_map. lock ( ) . get ( & key_hash) . cloned ( ) ;
61-
62- if let Some ( x) = vec {
63- for ( key, _) in & x {
64- let equals = jvm. invoke_virtual ( key, "equals" , "(Ljava/lang/Object;)Z" , ( ( * key) . clone ( ) , ) ) . await ?;
60+ let table = jvm. get_field ( & this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
61+ let table_len = jvm. array_length ( & table) . await ? as i32 ;
62+ let bucket_index = ( ( key_hash & 0x7FFFFFFF ) % table_len) as usize ;
63+
64+ let mut entry: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
65+ while !entry. is_null ( ) {
66+ let entry_hash: i32 = jvm. get_field ( & entry, "hash" , "I" ) . await ?;
67+ if entry_hash == key_hash {
68+ let entry_key: ClassInstanceRef < Object > = jvm. get_field ( & entry, "key" , "Ljava/lang/Object;" ) . await ?;
69+ let equals: bool = jvm. invoke_virtual ( & entry_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
6570 if equals {
6671 return Ok ( true ) ;
6772 }
6873 }
74+ entry = jvm. get_field ( & entry, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
6975 }
76+
7077 Ok ( false )
7178 }
7279
73- // TODO we need to add synchronized
7480 async fn get ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > , key : ClassInstanceRef < Object > ) -> Result < ClassInstanceRef < Object > > {
75- tracing:: debug!( "java.util.Hashtable::get({:?}, {:?})" , & this , & key ) ;
81+ tracing:: debug!( "java.util.Hashtable::get({this :?}, {key :?})" ) ;
7682
77- let rust_hash_map = Self :: get_rust_hashmap ( jvm, & this) . await ?;
7883 let key_hash: i32 = jvm. invoke_virtual ( & key, "hashCode" , "()I" , ( ) ) . await ?;
79-
80- let vec = rust_hash_map. lock ( ) . get ( & key_hash) . cloned ( ) ;
81-
82- if let Some ( x) = vec {
83- for ( key, value) in & x {
84- let equals = jvm. invoke_virtual ( key, "equals" , "(Ljava/lang/Object;)Z" , ( ( * key) . clone ( ) , ) ) . await ?;
84+ let table = jvm. get_field ( & this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
85+ let table_len = jvm. array_length ( & table) . await ? as i32 ;
86+ let bucket_index = ( ( key_hash & 0x7FFFFFFF ) % table_len) as usize ;
87+
88+ let mut entry: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
89+ while !entry. is_null ( ) {
90+ let entry_hash: i32 = jvm. get_field ( & entry, "hash" , "I" ) . await ?;
91+ if entry_hash == key_hash {
92+ let entry_key: ClassInstanceRef < Object > = jvm. get_field ( & entry, "key" , "Ljava/lang/Object;" ) . await ?;
93+ let equals: bool = jvm. invoke_virtual ( & entry_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
8594 if equals {
86- return Ok ( value . clone ( ) . into ( ) ) ;
95+ return jvm . get_field ( & entry , "value" , "Ljava/lang/Object;" ) . await ;
8796 }
8897 }
98+ entry = jvm. get_field ( & entry, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
8999 }
90100
91101 Ok ( None . into ( ) )
92102 }
93103
94- // TODO we need to add synchronized
95104 async fn remove (
96105 jvm : & Jvm ,
97106 _: & mut RuntimeContext ,
98- this : ClassInstanceRef < Self > ,
107+ mut this : ClassInstanceRef < Self > ,
99108 key : ClassInstanceRef < Object > ,
100109 ) -> Result < ClassInstanceRef < Object > > {
101- tracing:: debug!( "java.util.Hashtable::remove({:?}, {:?})" , & this , & key ) ;
110+ tracing:: debug!( "java.util.Hashtable::remove({this :?}, {key :?})" ) ;
102111
103- let rust_hash_map = Self :: get_rust_hashmap ( jvm, & this) . await ?;
104112 let key_hash: i32 = jvm. invoke_virtual ( & key, "hashCode" , "()I" , ( ) ) . await ?;
105-
106- let vec = rust_hash_map. lock ( ) . get ( & key_hash) . cloned ( ) ;
107-
108- if let Some ( x) = vec {
109- for ( i, ( bucket_key, _) ) in x. iter ( ) . enumerate ( ) {
110- let equals = jvm. invoke_virtual ( bucket_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
113+ let mut table = jvm. get_field ( & this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
114+ let table_len = jvm. array_length ( & table) . await ? as i32 ;
115+ let bucket_index = ( ( key_hash & 0x7FFFFFFF ) % table_len) as usize ;
116+
117+ let mut prev: ClassInstanceRef < HashtableEntry > = None . into ( ) ;
118+ let mut entry: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
119+
120+ while !entry. is_null ( ) {
121+ let entry_hash: i32 = jvm. get_field ( & entry, "hash" , "I" ) . await ?;
122+ if entry_hash == key_hash {
123+ let entry_key: ClassInstanceRef < Object > = jvm. get_field ( & entry, "key" , "Ljava/lang/Object;" ) . await ?;
124+ let equals: bool = jvm. invoke_virtual ( & entry_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
111125 if equals {
112- let ( _, old_value) = rust_hash_map. lock ( ) . get_mut ( & key_hash) . unwrap ( ) . remove ( i) ;
126+ let next: ClassInstanceRef < HashtableEntry > = jvm. get_field ( & entry, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
127+ if prev. is_null ( ) {
128+ jvm. store_array ( & mut table, bucket_index, core:: iter:: once ( next) ) . await ?;
129+ } else {
130+ jvm. put_field ( & mut prev, "next" , "Ljava/util/Hashtable$Entry;" , next) . await ?;
131+ }
113132
114- return Ok ( old_value. into ( ) ) ;
133+ let count: i32 = jvm. get_field ( & this, "count" , "I" ) . await ?;
134+ jvm. put_field ( & mut this, "count" , "I" , count - 1 ) . await ?;
135+
136+ return jvm. get_field ( & entry, "value" , "Ljava/lang/Object;" ) . await ;
115137 }
116138 }
139+ prev = entry;
140+ entry = jvm. get_field ( & prev, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
117141 }
118142
119143 Ok ( None . into ( ) )
120144 }
121145
122- // TODO we need to add synchronized
123146 async fn put (
124147 jvm : & Jvm ,
125148 _: & mut RuntimeContext ,
126- this : ClassInstanceRef < Self > ,
149+ mut this : ClassInstanceRef < Self > ,
127150 key : ClassInstanceRef < Object > ,
128151 value : ClassInstanceRef < Object > ,
129152 ) -> Result < ClassInstanceRef < Object > > {
130- tracing:: debug!( "java.util.Hashtable::put({:?}, {:?}, {:?})" , & this , & key , & value ) ;
153+ tracing:: debug!( "java.util.Hashtable::put({this :?}, {key :?}, {value :?})" ) ;
131154
132- let rust_hash_map = Self :: get_rust_hashmap ( jvm, & this) . await ?;
133155 let key_hash: i32 = jvm. invoke_virtual ( & key, "hashCode" , "()I" , ( ) ) . await ?;
134-
135- let vec = {
136- let mut rust_hash_map = rust_hash_map. lock ( ) ;
137- if !rust_hash_map. contains_key ( & key_hash) {
138- rust_hash_map. insert ( key_hash, Vec :: new ( ) ) ;
156+ let mut table = jvm. get_field ( & this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
157+ let table_len = jvm. array_length ( & table) . await ? as i32 ;
158+ let bucket_index = ( ( key_hash & 0x7FFFFFFF ) % table_len) as usize ;
159+
160+ let mut entry: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
161+ while !entry. is_null ( ) {
162+ let entry_hash: i32 = jvm. get_field ( & entry, "hash" , "I" ) . await ?;
163+ if entry_hash == key_hash {
164+ let entry_key: ClassInstanceRef < Object > = jvm. get_field ( & entry, "key" , "Ljava/lang/Object;" ) . await ?;
165+ let equals: bool = jvm. invoke_virtual ( & entry_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
166+ if equals {
167+ let old_value: ClassInstanceRef < Object > = jvm. get_field ( & entry, "value" , "Ljava/lang/Object;" ) . await ?;
168+ jvm. put_field ( & mut entry, "value" , "Ljava/lang/Object;" , value) . await ?;
169+ return Ok ( old_value) ;
170+ }
139171 }
172+ entry = jvm. get_field ( & entry, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
173+ }
174+
175+ let count: i32 = jvm. get_field ( & this, "count" , "I" ) . await ?;
176+ let threshold: i32 = jvm. get_field ( & this, "threshold" , "I" ) . await ?;
177+
178+ if count >= threshold {
179+ Self :: rehash ( jvm, & mut this) . await ?;
180+ table = jvm. get_field ( & this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
181+ let new_table_len = jvm. array_length ( & table) . await ? as i32 ;
182+ let new_bucket_index = ( ( key_hash & 0x7FFFFFFF ) % new_table_len) as usize ;
183+
184+ let existing: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, new_bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
185+ let new_entry = jvm
186+ . new_class (
187+ "java/util/Hashtable$Entry" ,
188+ "(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/Hashtable$Entry;)V" ,
189+ ( key_hash, key, value, existing) ,
190+ )
191+ . await ?;
192+ jvm. store_array ( & mut table, new_bucket_index, core:: iter:: once ( new_entry) ) . await ?;
193+ } else {
194+ let existing: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & table, bucket_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
195+ let new_entry = jvm
196+ . new_class (
197+ "java/util/Hashtable$Entry" ,
198+ "(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/Hashtable$Entry;)V" ,
199+ ( key_hash, key, value, existing) ,
200+ )
201+ . await ?;
202+ jvm. store_array ( & mut table, bucket_index, core:: iter:: once ( new_entry) ) . await ?;
203+ }
140204
141- rust_hash_map. get ( & key_hash) . cloned ( ) . unwrap ( )
142- } ;
205+ jvm. put_field ( & mut this, "count" , "I" , count + 1 ) . await ?;
206+
207+ Ok ( None . into ( ) )
208+ }
143209
144- for ( i, ( bucket_key, _) ) in vec. iter ( ) . enumerate ( ) {
145- let equals = jvm. invoke_virtual ( bucket_key, "equals" , "(Ljava/lang/Object;)Z" , ( key. clone ( ) , ) ) . await ?;
146- if equals {
147- let mut rust_hash_map = rust_hash_map. lock ( ) ;
148- let vec = rust_hash_map. get_mut ( & key_hash) . unwrap ( ) ;
210+ async fn rehash ( jvm : & Jvm , this : & mut ClassInstanceRef < Self > ) -> Result < ( ) > {
211+ let old_table = jvm. get_field ( this, "table" , "[Ljava/util/Hashtable$Entry;" ) . await ?;
212+ let old_capacity = jvm. array_length ( & old_table) . await ?;
213+ let new_capacity = old_capacity * 2 + 1 ;
149214
150- let ( _ , old_value ) = mem :: replace ( & mut vec [ i ] , ( key . into ( ) , value . into ( ) ) ) ;
215+ let mut new_table = jvm . instantiate_array ( "Ljava/util/Hashtable$Entry;" , new_capacity ) . await ? ;
151216
152- return Ok ( old_value. into ( ) ) ;
217+ for i in 0 ..old_capacity {
218+ let mut entry: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & old_table, i, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
219+ while !entry. is_null ( ) {
220+ let next: ClassInstanceRef < HashtableEntry > = jvm. get_field ( & entry, "next" , "Ljava/util/Hashtable$Entry;" ) . await ?;
221+ let entry_hash: i32 = jvm. get_field ( & entry, "hash" , "I" ) . await ?;
222+ let new_index = ( ( entry_hash & 0x7FFFFFFF ) % new_capacity as i32 ) as usize ;
223+
224+ let existing: ClassInstanceRef < HashtableEntry > = jvm. load_array ( & new_table, new_index, 1 ) . await ?. into_iter ( ) . next ( ) . unwrap ( ) ;
225+ jvm. put_field ( & mut entry, "next" , "Ljava/util/Hashtable$Entry;" , existing) . await ?;
226+ jvm. store_array ( & mut new_table, new_index, core:: iter:: once ( entry) ) . await ?;
227+
228+ entry = next;
153229 }
154230 }
155231
156- rust_hash_map. lock ( ) . get_mut ( & key_hash) . unwrap ( ) . push ( ( key. into ( ) , value. into ( ) ) ) ;
157-
158- Ok ( None . into ( ) )
159- }
232+ jvm. put_field ( this, "table" , "[Ljava/util/Hashtable$Entry;" , new_table) . await ?;
233+ jvm. put_field ( this, "threshold" , "I" , ( new_capacity as f32 * DEFAULT_LOAD_FACTOR ) as i32 ) . await ?;
160234
161- async fn get_rust_hashmap ( jvm : & Jvm , this : & ClassInstanceRef < Self > ) -> Result < RustHashMap > {
162- jvm. get_rust_object_field ( this, "raw" ) . await
235+ Ok ( ( ) )
163236 }
164237}
0 commit comments