11using System ;
22using System . Collections . Concurrent ;
33using System . Collections . Generic ;
4- using System . ComponentModel . Design ;
5- using System . Linq ;
6- using System . Net . Http . Headers ;
74using System . Reflection ;
8- using Google . Protobuf ;
9- using ClientApi ;
105using SpacetimeDB . SATS ;
6+ using System . Numerics ;
7+ using System . Runtime . CompilerServices ;
118
129namespace SpacetimeDB
1310{
@@ -19,19 +16,42 @@ public class ByteArrayComparer : IEqualityComparer<byte[]>
1916 {
2017 public bool Equals ( byte [ ] left , byte [ ] right )
2118 {
22- if ( left == null || right == null )
19+ if ( ReferenceEquals ( left , right ) )
2320 {
24- return left == right ;
21+ return true ;
2522 }
2623
27- return left . SequenceEqual ( right ) ;
24+ if ( left == null || right == null || left . Length != right . Length )
25+ {
26+ return false ;
27+ }
28+
29+ return EqualsUnvectorized ( left , right ) ;
30+
31+ }
32+
33+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
34+ private bool EqualsUnvectorized ( byte [ ] left , byte [ ] right )
35+ {
36+ for ( int i = 0 ; i < left . Length ; i ++ )
37+ {
38+ if ( left [ i ] != right [ i ] )
39+ {
40+ return false ;
41+ }
42+ }
43+
44+ return true ;
2845 }
2946
30- public int GetHashCode ( byte [ ] key )
47+ public int GetHashCode ( byte [ ] obj )
3148 {
32- if ( key == null )
33- throw new ArgumentNullException ( nameof ( key ) ) ;
34- return key . Sum ( b => b ) ;
49+ int hash = 17 ;
50+ foreach ( byte b in obj )
51+ {
52+ hash = hash * 31 + b ;
53+ }
54+ return hash ;
3555 }
3656 }
3757
@@ -45,9 +65,6 @@ public int GetHashCode(byte[] key)
4565 // Maps from primary key to type value
4666 public readonly Dictionary < byte [ ] , ( AlgebraicValue , object ) > entries ;
4767
48- // Maps from primary key to decoded value
49- public readonly ConcurrentDictionary < byte [ ] , ( AlgebraicValue , object ) > decodedValues ;
50-
5168 public Type ClientTableType
5269 {
5370 get => clientTableType ;
@@ -97,48 +114,11 @@ public TableCache(Type clientTableType, AlgebraicType rowSchema, Func<AlgebraicV
97114 GetPrimaryKeyTypeFunc = ( Func < AlgebraicType , AlgebraicType > ) clientTableType . GetMethod ( "GetPrimaryKeyType" , BindingFlags . Static | BindingFlags . Public )
98115 ? . CreateDelegate ( typeof ( Func < AlgebraicType , AlgebraicType > ) ) ;
99116 entries = new Dictionary < byte [ ] , ( AlgebraicValue , object ) > ( new ByteArrayComparer ( ) ) ;
100- decodedValues = new ConcurrentDictionary < byte [ ] , ( AlgebraicValue , object ) > ( new ByteArrayComparer ( ) ) ;
101- }
102-
103- public bool GetDecodedValue ( byte [ ] pk , out AlgebraicValue value , out object obj )
104- {
105- if ( decodedValues . TryGetValue ( pk , out var decoded ) )
106- {
107- value = decoded . Item1 ;
108- obj = decoded . Item2 ;
109- return true ;
110- }
111-
112- value = null ;
113- obj = null ;
114- return false ;
115117 }
116118
117119 /// <summary>
118120 /// Decodes the given AlgebraicValue into the out parameter `obj`.
119121 /// </summary>
120- /// <param name="pk">The primary key of the row associated with `value`.</param>
121- /// <param name="value">The AlgebraicValue to decode.</param>
122- /// <param name="obj">The domain object for `value`</param>
123- public void SetDecodedValue ( byte [ ] pk , AlgebraicValue value , out object obj )
124- {
125- if ( decodedValues . TryGetValue ( pk , out var existingObj ) )
126- {
127- obj = existingObj . Item2 ;
128- }
129- else
130- {
131- var decoded = ( value , decoderFunc ( value ) ) ;
132- decodedValues [ pk ] = decoded ;
133- obj = decoded . Item2 ;
134- }
135- }
136-
137- /// <summary>
138- /// Decodes the given AlgebraicValue into the out parameter `obj`.
139- /// Does NOT cache the resulting value! This should only be used with rows
140- /// that don't participate in the usual client cache lifecycle, i.e. OneOffQuery.
141- /// </summary>
142122 /// <param name="value">The AlgebraicValue to decode.</param>
143123 /// <param name="obj">The domain object for `value`</param>
144124 public void SetAndForgetDecodedValue ( AlgebraicValue value , out object obj )
@@ -149,63 +129,34 @@ public void SetAndForgetDecodedValue(AlgebraicValue value, out object obj)
149129 /// <summary>
150130 /// Inserts the value into the table. There can be no existing value with the provided pk.
151131 /// </summary>
152- /// <returns></returns>
153- public object InsertEntry ( byte [ ] rowPk )
132+ /// <returns>True if the row was inserted, false if the row wasn't inserted because it was a duplicate. </returns>
133+ public bool InsertEntry ( byte [ ] rowPk , AlgebraicValue value )
154134 {
155- if ( entries . TryGetValue ( rowPk , out var existingValue ) )
156- {
157- // Debug.LogWarning($"We tried to insert a database row that already exists. table={Name} RowPK={Convert.ToBase64String(rowPk)}");
158- return existingValue . Item2 ;
159- }
160-
161- if ( GetDecodedValue ( rowPk , out var value , out var obj ) )
135+ if ( entries . ContainsKey ( rowPk ) )
162136 {
163- entries [ rowPk ] = ( value , obj ) ;
164- return obj ;
137+ return false ;
165138 }
166-
167- // Read failure
168- SpacetimeDBClient . instance . Logger . LogError (
169- $ "Read error when converting row value for table: { clientTableType . Name } rowPk={ Convert . ToBase64String ( rowPk ) } (version issue?)") ;
170- return null ;
171- }
172-
173- /// <summary>
174- /// Updates an entry. Returns whether or not the update was successful. Updates only succeed if
175- /// a previous value was overwritten.
176- /// </summary>
177- /// <param name="pk">The primary key that uniquely identifies this row</param>
178- /// <param name="newValueByteString">The new for the table entry</param>
179- /// <returns>True when the old value was removed and the new value was inserted.</returns>
180- public bool UpdateEntry ( ByteString pk , ByteString newValueByteString )
181- {
182- // We have to figure out if pk is going to change or not
183- throw new InvalidOperationException ( ) ;
139+
140+ // Insert the row into our table
141+ entries [ rowPk ] = ( value , decoderFunc ( value ) ) ;
142+ return true ;
184143 }
185144
186145 /// <summary>
187146 /// Deletes a value from the table.
188147 /// </summary>
189148 /// <param name="rowPk">The primary key that uniquely identifies this row</param>
190149 /// <returns></returns>
191- public object DeleteEntry ( byte [ ] rowPk )
150+ public bool DeleteEntry ( byte [ ] rowPk )
192151 {
193152 if ( entries . TryGetValue ( rowPk , out var value ) )
194153 {
195154 entries . Remove ( rowPk ) ;
196- return value . Item2 ;
197- }
198-
199- // SpacetimeDB is asking us to delete something we don't have, this makes no sense. We can
200- // fabricate the deletion by trying to look it up in our local decode table.
201- if ( decodedValues . TryGetValue ( rowPk , out var decodedValue ) )
202- {
203- SpacetimeDBClient . instance . Logger . LogWarning ( "Deleting value that we don't have (using cached value)" ) ;
204- return decodedValue . Item2 ;
155+ return true ;
205156 }
206157
207158 SpacetimeDBClient . instance . Logger . LogWarning ( "Deleting value that we don't have (no cached value available)" ) ;
208- return null ;
159+ return false ;
209160 }
210161
211162 /// <summary>
@@ -235,23 +186,9 @@ public AlgebraicValue GetPrimaryKeyValue(AlgebraicValue row)
235186 return GetPrimaryKeyValueFunc != null ? GetPrimaryKeyValueFunc . Invoke ( row ) : null ;
236187 }
237188
238- public AlgebraicType GetPrimaryKeyType ( AlgebraicType row )
239- {
240- return GetPrimaryKeyTypeFunc != null ? GetPrimaryKeyTypeFunc . Invoke ( row ) : null ;
241- }
242-
243- public bool ComparePrimaryKey ( byte [ ] rowPk1 , byte [ ] rowPk2 )
189+ public AlgebraicType GetPrimaryKeyType ( )
244190 {
245- if ( ! decodedValues . TryGetValue ( rowPk1 , out var v1 ) )
246- {
247- return false ;
248- }
249- if ( ! decodedValues . TryGetValue ( rowPk2 , out var v2 ) )
250- {
251- return false ;
252- }
253-
254- return ( bool ) ComparePrimaryKeyFunc . Invoke ( rowSchema , v1 . Item1 , v2 . Item1 ) ;
191+ return GetPrimaryKeyTypeFunc != null ? GetPrimaryKeyTypeFunc . Invoke ( rowSchema ) : null ;
255192 }
256193 }
257194
@@ -320,5 +257,7 @@ public int Count(string name)
320257 }
321258
322259 public IEnumerable < string > GetTableNames ( ) => tables . Keys ;
260+
261+ public IEnumerable < TableCache > GetTables ( ) => tables . Values ;
323262 }
324263}
0 commit comments