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 ),
@@ -148,25 +161,86 @@ public DefaultCodecs(ByteBufAllocator byteBufAllocator, boolean preferAttachedBu
148
161
this .codecs .addAll (defaultArrayCodecs );
149
162
}
150
163
164
+ void invalidateCaches () {
165
+ this .decodeCodecsCache .clear ();
166
+ this .encodeCodecsCache .clear ();
167
+ this .encodeNullCodecsCache .clear ();
168
+ }
169
+
151
170
@ Override
152
171
public void addFirst (Codec <?> codec ) {
153
172
Assert .requireNonNull (codec , "codec must not be null" );
154
- synchronized (this .codecs ) {
155
- this .codecs .add (0 , codec );
156
- }
173
+ invalidateCaches ();
174
+ this .codecs .add (0 , codec );
157
175
}
158
176
159
177
@ Override
160
178
public void addLast (Codec <?> codec ) {
161
179
Assert .requireNonNull (codec , "codec must not be null" );
162
- synchronized (this .codecs ) {
163
- this .codecs .add (codec );
180
+ invalidateCaches ();
181
+ this .codecs .add (codec );
182
+ }
183
+
184
+ private <T > int generateCodecHash (int dataType , Format format , Class <? extends T > type ) {
185
+ int hash = (dataType << 5 ) - dataType ;
186
+ hash = (hash << 5 ) - hash + format .hashCode ();
187
+ hash = (hash << 5 ) - hash + generateCodecHash (type );
188
+ return hash ;
189
+ }
190
+
191
+ private <T > int generateCodecHash (Class <? extends T > type ) {
192
+ int hash = type .hashCode ();
193
+ if (type .getComponentType () != null ) {
194
+ hash = (hash << 5 ) - hash + generateCodecHash (type .getComponentType ());
164
195
}
196
+ return hash ;
197
+ }
198
+
199
+ @ Nullable
200
+ @ SuppressWarnings ("rawtypes" )
201
+ Codec findCodec (int codecHash , Map <Integer , Codec <?>> cache , Predicate <Codec <?>> predicate ) {
202
+ Codec <?> found = cache .get (codecHash );
203
+ if (found == null ) {
204
+ for (Codec <?> codec : this .codecs ) {
205
+ if (predicate .test (codec )) {
206
+ found = codec ;
207
+ cache .put (codecHash , found );
208
+ break ;
209
+ }
210
+ }
211
+ }
212
+ return found ;
165
213
}
166
214
167
- @ Override
168
215
@ Nullable
169
216
@ SuppressWarnings ("unchecked" )
217
+ <T > Codec <T > findDecodeCodec (int dataType , Format format , Class <? extends T > type ) {
218
+ return findCodec (
219
+ generateCodecHash (dataType , format , type ),
220
+ decodeCodecsCache ,
221
+ codec -> codec .canDecode (dataType , format , type ));
222
+ }
223
+
224
+ @ Nullable
225
+ @ SuppressWarnings ("rawtypes" )
226
+ Codec findEncodeCodec (Object value ) {
227
+ return findCodec (
228
+ generateCodecHash (value .getClass ()),
229
+ encodeCodecsCache ,
230
+ codec -> codec .canEncode (value ));
231
+ }
232
+
233
+ @ Nullable
234
+ @ SuppressWarnings ("rawtypes" )
235
+ Codec findEncodeNullCodec (Class <?> type ) {
236
+ return findCodec (
237
+ generateCodecHash (type ),
238
+ encodeNullCodecsCache ,
239
+ codec -> codec .canEncodeNull (type ));
240
+ }
241
+
242
+ @ Override
243
+ @ Nullable
170
244
public <T > T decode (@ Nullable ByteBuf buffer , int dataType , Format format , Class <? extends T > type ) {
171
245
Assert .requireNonNull (format , "format must not be null" );
172
246
Assert .requireNonNull (type , "type must not be null" );
@@ -175,10 +249,9 @@ public <T> T decode(@Nullable ByteBuf buffer, int dataType, Format format, Class
175
249
return null ;
176
250
}
177
251
178
- for (Codec <?> codec : this .codecs ) {
179
- if (codec .canDecode (dataType , format , type )) {
180
- return ((Codec <T >) codec ).decode (buffer , dataType , format , type );
181
- }
252
+ Codec <T > codec = findDecodeCodec (dataType , format , type );
253
+ if (codec != null ) {
254
+ return codec .decode (buffer , dataType , format , type );
182
255
}
183
256
184
257
throw new IllegalArgumentException (String .format ("Cannot decode value of type %s with OID %d" , type .getName (), dataType ));
@@ -201,37 +274,37 @@ public EncodedParameter encode(Object value) {
201
274
}
202
275
203
276
if (parameter .getType () instanceof R2dbcType ) {
204
-
205
- PostgresqlObjectId targetType = PostgresqlObjectId .valueOf ((R2dbcType ) parameter .getType ());
206
- dataType = targetType ;
277
+ dataType = PostgresqlObjectId .valueOf ((R2dbcType ) parameter .getType ());
207
278
}
208
279
209
280
if (parameter .getType () instanceof PostgresTypeIdentifier ) {
210
281
dataType = (PostgresTypeIdentifier ) parameter .getType ();
211
282
}
212
283
}
213
284
285
+ return encodeParameterValue (value , dataType , parameterValue );
286
+ }
287
+
288
+ EncodedParameter encodeParameterValue (Object value , @ Nullable PostgresTypeIdentifier dataType , @ Nullable Object parameterValue ) {
214
289
if (dataType == null ) {
215
290
216
291
if (parameterValue == null ) {
217
292
throw new IllegalArgumentException (String .format ("Cannot encode null value %s using type inference" , value ));
218
293
}
219
294
220
- for (Codec <?> codec : this .codecs ) {
221
- if (codec .canEncode (parameterValue )) {
222
- return codec .encode (parameterValue );
223
- }
295
+ Codec <?> codec = findEncodeCodec (parameterValue );
296
+ if (codec != null ) {
297
+ return codec .encode (parameterValue );
224
298
}
225
299
} else {
226
300
227
301
if (parameterValue == null ) {
228
302
return new EncodedParameter (Format .FORMAT_BINARY , dataType .getObjectId (), NULL_VALUE );
229
303
}
230
304
231
- for (Codec <?> codec : this .codecs ) {
232
- if (codec .canEncode (parameterValue )) {
233
- return codec .encode (parameterValue , dataType .getObjectId ());
234
- }
305
+ Codec <?> codec = findEncodeCodec (parameterValue );
306
+ if (codec != null ) {
307
+ return codec .encode (parameterValue , dataType .getObjectId ());
235
308
}
236
309
}
237
310
@@ -242,10 +315,9 @@ public EncodedParameter encode(Object value) {
242
315
public EncodedParameter encodeNull (Class <?> type ) {
243
316
Assert .requireNonNull (type , "type must not be null" );
244
317
245
- for (Codec <?> codec : this .codecs ) {
246
- if (codec .canEncodeNull (type )) {
247
- return codec .encodeNull ();
248
- }
318
+ Codec <?> codec = findEncodeNullCodec (type );
319
+ if (codec != null ) {
320
+ return codec .encodeNull ();
249
321
}
250
322
251
323
throw new IllegalArgumentException (String .format ("Cannot encode null parameter of type %s" , type .getName ()));
@@ -255,20 +327,17 @@ public EncodedParameter encodeNull(Class<?> type) {
255
327
public Class <?> preferredType (int dataType , Format format ) {
256
328
Assert .requireNonNull (format , "format must not be null" );
257
329
258
- for (Codec <?> codec : this .codecs ) {
259
- if (codec .canDecode (dataType , format , Object .class )) {
260
- return codec .type ();
261
- }
330
+ Codec <?> codec = findDecodeCodec (dataType , format , Object .class );
331
+ if (codec != null ) {
332
+ return codec .type ();
262
333
}
263
334
264
335
return null ;
265
336
}
266
337
267
338
@ Override
268
339
public Iterator <Codec <?>> iterator () {
269
- synchronized (this .codecs ) {
270
- return Collections .unmodifiableList (new ArrayList <>(this .codecs )).iterator ();
271
- }
340
+ return Collections .unmodifiableList (new ArrayList <>(this .codecs )).iterator ();
272
341
}
273
342
274
343
}
0 commit comments