31
31
import java .util .Collections ;
32
32
import java .util .Iterator ;
33
33
import java .util .List ;
34
+ import java .util .Map ;
35
+ import java .util .concurrent .ConcurrentHashMap ;
36
+ import java .util .concurrent .CopyOnWriteArrayList ;
37
+ import java .util .function .Predicate ;
34
38
35
39
import static io .r2dbc .postgresql .client .EncodedParameter .NULL_VALUE ;
36
40
@@ -43,6 +47,12 @@ public final class DefaultCodecs implements Codecs, CodecRegistry {
43
47
44
48
private final List <Codec <?>> codecs ;
45
49
50
+ private final Map <Integer , Codec <?>> decodeCodecsCache ;
51
+
52
+ private final Map <Integer , Codec <?>> encodeCodecsCache ;
53
+
54
+ private final Map <Integer , Codec <?>> encodeNullCodecsCache ;
55
+
46
56
/**
47
57
* Create a new instance of {@link DefaultCodecs} preferring detached (copied buffers).
48
58
*
@@ -61,8 +71,11 @@ public DefaultCodecs(ByteBufAllocator byteBufAllocator) {
61
71
@ SuppressWarnings ({"unchecked" , "rawtypes" })
62
72
public DefaultCodecs (ByteBufAllocator byteBufAllocator , boolean preferAttachedBuffers ) {
63
73
Assert .requireNonNull (byteBufAllocator , "byteBufAllocator must not be null" );
74
+ this .decodeCodecsCache = new ConcurrentHashMap <>();
75
+ this .encodeCodecsCache = new ConcurrentHashMap <>();
76
+ this .encodeNullCodecsCache = new ConcurrentHashMap <>();
64
77
65
- this .codecs = new ArrayList <>(Arrays .asList (
78
+ this .codecs = new CopyOnWriteArrayList <>(Arrays .asList (
66
79
67
80
// Prioritized Codecs
68
81
new StringCodec (byteBufAllocator ),
@@ -144,25 +157,86 @@ public DefaultCodecs(ByteBufAllocator byteBufAllocator, boolean preferAttachedBu
144
157
this .codecs .addAll (defaultArrayCodecs );
145
158
}
146
159
160
+ void invalidateCaches () {
161
+ this .decodeCodecsCache .clear ();
162
+ this .encodeCodecsCache .clear ();
163
+ this .encodeNullCodecsCache .clear ();
164
+ }
165
+
147
166
@ Override
148
167
public void addFirst (Codec <?> codec ) {
149
168
Assert .requireNonNull (codec , "codec must not be null" );
150
- synchronized (this .codecs ) {
151
- this .codecs .add (0 , codec );
152
- }
169
+ invalidateCaches ();
170
+ this .codecs .add (0 , codec );
153
171
}
154
172
155
173
@ Override
156
174
public void addLast (Codec <?> codec ) {
157
175
Assert .requireNonNull (codec , "codec must not be null" );
158
- synchronized (this .codecs ) {
159
- this .codecs .add (codec );
176
+ invalidateCaches ();
177
+ this .codecs .add (codec );
178
+ }
179
+
180
+ private <T > int generateCodecHash (int dataType , Format format , Class <? extends T > type ) {
181
+ int hash = (dataType << 5 ) - dataType ;
182
+ hash = (hash << 5 ) - hash + format .hashCode ();
183
+ hash = (hash << 5 ) - hash + generateCodecHash (type );
184
+ return hash ;
185
+ }
186
+
187
+ private <T > int generateCodecHash (Class <? extends T > type ) {
188
+ int hash = type .hashCode ();
189
+ if (type .getComponentType () != null ) {
190
+ hash = (hash << 5 ) - hash + generateCodecHash (type .getComponentType ());
160
191
}
192
+ return hash ;
193
+ }
194
+
195
+ @ Nullable
196
+ @ SuppressWarnings ("rawtypes" )
197
+ Codec findCodec (int codecHash , Map <Integer , Codec <?>> cache , Predicate <Codec <?>> predicate ) {
198
+ Codec <?> found = cache .get (codecHash );
199
+ if (found == null ) {
200
+ for (Codec <?> codec : this .codecs ) {
201
+ if (predicate .test (codec )) {
202
+ found = codec ;
203
+ cache .put (codecHash , found );
204
+ break ;
205
+ }
206
+ }
207
+ }
208
+ return found ;
161
209
}
162
210
163
- @ Override
164
211
@ Nullable
165
212
@ SuppressWarnings ("unchecked" )
213
+ <T > Codec <T > findDecodeCodec (int dataType , Format format , Class <? extends T > type ) {
214
+ return findCodec (
215
+ generateCodecHash (dataType , format , type ),
216
+ decodeCodecsCache ,
217
+ codec -> codec .canDecode (dataType , format , type ));
218
+ }
219
+
220
+ @ Nullable
221
+ @ SuppressWarnings ("rawtypes" )
222
+ Codec findEncodeCodec (Object value ) {
223
+ return findCodec (
224
+ generateCodecHash (value .getClass ()),
225
+ encodeCodecsCache ,
226
+ codec -> codec .canEncode (value ));
227
+ }
228
+
229
+ @ Nullable
230
+ @ SuppressWarnings ("rawtypes" )
231
+ Codec findEncodeNullCodec (Class <?> type ) {
232
+ return findCodec (
233
+ generateCodecHash (type ),
234
+ encodeNullCodecsCache ,
235
+ codec -> codec .canEncodeNull (type ));
236
+ }
237
+
238
+ @ Override
239
+ @ Nullable
166
240
public <T > T decode (@ Nullable ByteBuf buffer , int dataType , Format format , Class <? extends T > type ) {
167
241
Assert .requireNonNull (format , "format must not be null" );
168
242
Assert .requireNonNull (type , "type must not be null" );
@@ -171,10 +245,9 @@ public <T> T decode(@Nullable ByteBuf buffer, int dataType, Format format, Class
171
245
return null ;
172
246
}
173
247
174
- for (Codec <?> codec : this .codecs ) {
175
- if (codec .canDecode (dataType , format , type )) {
176
- return ((Codec <T >) codec ).decode (buffer , dataType , format , type );
177
- }
248
+ Codec <T > codec = findDecodeCodec (dataType , format , type );
249
+ if (codec != null ) {
250
+ return codec .decode (buffer , dataType , format , type );
178
251
}
179
252
180
253
throw new IllegalArgumentException (String .format ("Cannot decode value of type %s with OID %d" , type .getName (), dataType ));
@@ -197,37 +270,37 @@ public EncodedParameter encode(Object value) {
197
270
}
198
271
199
272
if (parameter .getType () instanceof R2dbcType ) {
200
-
201
- PostgresqlObjectId targetType = PostgresqlObjectId .valueOf ((R2dbcType ) parameter .getType ());
202
- dataType = targetType ;
273
+ dataType = PostgresqlObjectId .valueOf ((R2dbcType ) parameter .getType ());
203
274
}
204
275
205
276
if (parameter .getType () instanceof PostgresTypeIdentifier ) {
206
277
dataType = (PostgresTypeIdentifier ) parameter .getType ();
207
278
}
208
279
}
209
280
281
+ return encodeParameterValue (value , dataType , parameterValue );
282
+ }
283
+
284
+ EncodedParameter encodeParameterValue (Object value , @ Nullable PostgresTypeIdentifier dataType , @ Nullable Object parameterValue ) {
210
285
if (dataType == null ) {
211
286
212
287
if (parameterValue == null ) {
213
288
throw new IllegalArgumentException (String .format ("Cannot encode null value %s using type inference" , value ));
214
289
}
215
290
216
- for (Codec <?> codec : this .codecs ) {
217
- if (codec .canEncode (parameterValue )) {
218
- return codec .encode (parameterValue );
219
- }
291
+ Codec <?> codec = findEncodeCodec (parameterValue );
292
+ if (codec != null ) {
293
+ return codec .encode (parameterValue );
220
294
}
221
295
} else {
222
296
223
297
if (parameterValue == null ) {
224
298
return new EncodedParameter (Format .FORMAT_BINARY , dataType .getObjectId (), NULL_VALUE );
225
299
}
226
300
227
- for (Codec <?> codec : this .codecs ) {
228
- if (codec .canEncode (parameterValue )) {
229
- return codec .encode (parameterValue , dataType .getObjectId ());
230
- }
301
+ Codec <?> codec = findEncodeCodec (parameterValue );
302
+ if (codec != null ) {
303
+ return codec .encode (parameterValue , dataType .getObjectId ());
231
304
}
232
305
}
233
306
@@ -238,10 +311,9 @@ public EncodedParameter encode(Object value) {
238
311
public EncodedParameter encodeNull (Class <?> type ) {
239
312
Assert .requireNonNull (type , "type must not be null" );
240
313
241
- for (Codec <?> codec : this .codecs ) {
242
- if (codec .canEncodeNull (type )) {
243
- return codec .encodeNull ();
244
- }
314
+ Codec <?> codec = findEncodeNullCodec (type );
315
+ if (codec != null ) {
316
+ return codec .encodeNull ();
245
317
}
246
318
247
319
throw new IllegalArgumentException (String .format ("Cannot encode null parameter of type %s" , type .getName ()));
@@ -251,20 +323,17 @@ public EncodedParameter encodeNull(Class<?> type) {
251
323
public Class <?> preferredType (int dataType , Format format ) {
252
324
Assert .requireNonNull (format , "format must not be null" );
253
325
254
- for (Codec <?> codec : this .codecs ) {
255
- if (codec .canDecode (dataType , format , Object .class )) {
256
- return codec .type ();
257
- }
326
+ Codec <?> codec = findDecodeCodec (dataType , format , Object .class );
327
+ if (codec != null ) {
328
+ return codec .type ();
258
329
}
259
330
260
331
return null ;
261
332
}
262
333
263
334
@ Override
264
335
public Iterator <Codec <?>> iterator () {
265
- synchronized (this .codecs ) {
266
- return Collections .unmodifiableList (new ArrayList <>(this .codecs )).iterator ();
267
- }
336
+ return Collections .unmodifiableList (new ArrayList <>(this .codecs )).iterator ();
268
337
}
269
338
270
339
}
0 commit comments