@@ -742,6 +742,8 @@ object JsonCodecMaker {
742742 def isCollection (tpe : Type ): Boolean =
743743 tpe <:< typeOf[Iterable [? ]] || tpe <:< typeOf[Iterator [? ]] || tpe <:< typeOf[Array [? ]]
744744
745+ def isJavaEnum (tpe : Type ): Boolean = tpe <:< typeOf[java.lang.Enum [? ]]
746+
745747 def scalaCollectionCompanion (tpe : Type ): Tree =
746748 if (tpe.typeSymbol.fullName.startsWith(" scala.collection." )) Ident (tpe.typeSymbol.companion)
747749 else fail(s " Unsupported type ' $tpe'. Please consider using a custom implicitly accessible codec for it. " )
@@ -808,26 +810,43 @@ object JsonCodecMaker {
808810 (name, q " private[this] val $name = new _root_.java.util.concurrent.ConcurrentHashMap[ $keyTpe, $tpe] " )
809811 })._1)
810812
811- case class JavaEnumValueInfo ( value : Tree , name : String , transformed : Boolean )
813+ sealed trait TypeInfo
812814
813- val enumValueInfos = new mutable. LinkedHashMap [ Type , List [ JavaEnumValueInfo ]]
815+ case class JavaEnumValueInfo ( value : Tree , name : String )
814816
815- def isJavaEnum (tpe : Type ): Boolean = tpe <:< typeOf[java.lang.Enum [? ]]
817+ case class JavaEnumInfo (valueInfos : List [JavaEnumValueInfo ], hasTransformed : Boolean , doEncoding : Boolean ) extends TypeInfo {
818+ val doLinearSearch : Boolean = valueInfos.size <= 8 && valueInfos.foldLeft(0 )(_ + _.name.length) <= 64
819+ }
820+
821+ case class FieldInfo (symbol : TermSymbol , mappedName : String , tmpName : TermName , getter : MethodSymbol ,
822+ defaultValue : Option [Tree ], resolvedTpe : Type , isStringified : Boolean )
823+
824+ case class ClassInfo (tpe : Type , paramLists : List [List [FieldInfo ]]) extends TypeInfo {
825+ val fields : List [FieldInfo ] = paramLists.flatten
826+ }
816827
817- def javaEnumValues (tpe : Type ): List [JavaEnumValueInfo ] = enumValueInfos.getOrElseUpdate(tpe, {
828+ val typeInfos = new mutable.HashMap [Type , TypeInfo ]
829+
830+ def getJavaEnumInfo (tpe : Type ): JavaEnumInfo = typeInfos.getOrElseUpdate(tpe, {
818831 val javaEnumValueNameMapper : String => String = n => cfg.javaEnumValueNameMapper.lift(n).getOrElse(n)
832+ var hasTransformed = false
833+ var doEncoding = false
819834 var values = tpe.typeSymbol.asClass.knownDirectSubclasses.toList.map { s : Symbol =>
820835 val name = s.name.toString
821836 val transformedName = javaEnumValueNameMapper(name)
822- JavaEnumValueInfo (q " $s" , transformedName, name != transformedName)
837+ hasTransformed |= name != transformedName
838+ doEncoding |= isEncodingRequired(transformedName)
839+ new JavaEnumValueInfo (q " $s" , transformedName)
823840 }
824841 if (values eq Nil ) {
825842 val comp = companion(tpe)
826843 values =
827844 comp.typeSignature.members.sorted.collect { case m : MethodSymbol if m.isGetter && m.returnType.dealias =:= tpe =>
828845 val name = decodeName(m)
829846 val transformedName = javaEnumValueNameMapper(name)
830- JavaEnumValueInfo (q " $comp. ${TermName (name)}" , transformedName, name != transformedName)
847+ hasTransformed |= name != transformedName
848+ doEncoding |= isEncodingRequired(transformedName)
849+ new JavaEnumValueInfo (q " $comp. ${TermName (name)}" , transformedName)
831850 }
832851 }
833852 val nameCollisions = duplicated(values.map(_.name))
@@ -837,19 +856,19 @@ object JsonCodecMaker {
837856 s " names of the enum that are mapped by the ' ${typeOf[CodecMakerConfig ]}.javaEnumValueNameMapper' function. " +
838857 s " Result values should be unique per enum class. " )
839858 }
840- values
841- })
859+ new JavaEnumInfo ( values, hasTransformed, doEncoding)
860+ }). asInstanceOf [ JavaEnumInfo ]
842861
843- def genReadEnumValue (enumValues : Seq [ JavaEnumValueInfo ] , unexpectedEnumValueHandler : Tree ): Tree = {
862+ def genReadEnumValue (enumInfo : JavaEnumInfo , unexpectedEnumValueHandler : Tree ): Tree = {
844863 def genReadCollisions (es : collection.Seq [JavaEnumValueInfo ]): Tree =
845864 es.foldRight(unexpectedEnumValueHandler) { (e, acc) =>
846865 q " if (in.isCharBufEqualsTo(l, ${e.name})) ${e.value} else $acc"
847866 }
848867
849- if (enumValues.size <= 8 && enumValues.foldLeft( 0 )(_ + _.name.length) <= 64 ) genReadCollisions(enumValues )
868+ if (enumInfo.doLinearSearch) genReadCollisions(enumInfo.valueInfos )
850869 else {
851870 val hashCode = (e : JavaEnumValueInfo ) => JsonReader .toHashCode(e.name.toCharArray, e.name.length)
852- val cases = groupByOrdered(enumValues )(hashCode).map { case (hash, fs) =>
871+ val cases = groupByOrdered(enumInfo.valueInfos )(hashCode).map { case (hash, fs) =>
853872 cq " $hash => ${genReadCollisions(fs)}"
854873 } :+ cq " _ => $unexpectedEnumValueHandler"
855874 q """ (in.charBufToHashCode(l): @_root_.scala.annotation.switch) match {
@@ -858,16 +877,7 @@ object JsonCodecMaker {
858877 }
859878 }
860879
861- case class FieldInfo (symbol : TermSymbol , mappedName : String , tmpName : TermName , getter : MethodSymbol ,
862- defaultValue : Option [Tree ], resolvedTpe : Type , isStringified : Boolean )
863-
864- case class ClassInfo (tpe : Type , paramLists : List [List [FieldInfo ]]) {
865- val fields : List [FieldInfo ] = paramLists.flatten
866- }
867-
868- val classInfos = new mutable.LinkedHashMap [Type , ClassInfo ]
869-
870- def getClassInfo (tpe : Type ): ClassInfo = classInfos.getOrElseUpdate(tpe, {
880+ def getClassInfo (tpe : Type ): ClassInfo = typeInfos.getOrElseUpdate(tpe, {
871881 case class FieldAnnotations (partiallyMappedName : Option [String ], transient : Boolean , stringified : Boolean )
872882
873883 def hasSupportedAnnotation (m : TermSymbol ): Boolean = {
@@ -930,7 +940,7 @@ object JsonCodecMaker {
930940 }
931941 })
932942 })
933- })
943+ }). asInstanceOf [ ClassInfo ]
934944
935945 def isValueClass (tpe : Type ): Boolean = ! isConstType(tpe) && isNonAbstractScalaClass(tpe) &&
936946 (tpe.typeSymbol.asClass.isDerivedValueClass || cfg.inlineOneValueClasses && ! isCollection(tpe) && getClassInfo(tpe).fields.size == 1 )
@@ -1027,7 +1037,7 @@ object JsonCodecMaker {
10271037 }
10281038 } else if (isJavaEnum(tpe)) {
10291039 q """ val l = in.readKeyAsCharBuf()
1030- ${genReadEnumValue(javaEnumValues (tpe), q " in.enumValueError(l) " )}"""
1040+ ${genReadEnumValue(getJavaEnumInfo (tpe), q " in.enumValueError(l) " )}"""
10311041 } else if (isConstType(tpe)) {
10321042 tpe match {
10331043 case ConstantType (Constant (v : String )) =>
@@ -1187,15 +1197,14 @@ object JsonCodecMaker {
11871197 if (cfg.useScalaEnumValueId) q " out.writeKey( $x.id) "
11881198 else q " out.writeKey( $x.toString) "
11891199 } else if (isJavaEnum(tpe)) {
1190- val es = javaEnumValues(tpe)
1191- val encodingRequired = es.exists(e => isEncodingRequired(e.name))
1192- if (es.exists(_.transformed)) {
1193- val cases = es.map(e => cq " ${e.value} => ${e.name}" ) :+
1200+ val enumInfo = getJavaEnumInfo(tpe)
1201+ if (enumInfo.hasTransformed) {
1202+ val cases = enumInfo.valueInfos.map(e => cq " ${e.value} => ${e.name}" ) :+
11941203 cq """ _ => out.encodeError("illegal enum value: " + $x) """
1195- if (encodingRequired ) q " out.writeKey( $x match { case .. $cases }) "
1204+ if (enumInfo.doEncoding ) q " out.writeKey( $x match { case .. $cases }) "
11961205 else q " out.writeNonEscapedAsciiKey( $x match { case .. $cases }) "
11971206 } else {
1198- if (encodingRequired ) q " out.writeKey( $x.name) "
1207+ if (enumInfo.doEncoding ) q " out.writeKey( $x.name) "
11991208 else q " out.writeNonEscapedAsciiKey( $x.name) "
12001209 }
12011210 } else if (isConstType(tpe)) {
@@ -1378,28 +1387,27 @@ object JsonCodecMaker {
13781387 case class MethodKey (tpe : Type , isStringified : Boolean , discriminator : Tree )
13791388
13801389 val decodeMethodNames = new mutable.HashMap [MethodKey , TermName ]
1381- val decodeMethodTrees = new mutable.ArrayBuffer [Tree ]
1390+ val methodTrees = new mutable.ArrayBuffer [Tree ]
13821391
13831392 def withDecoderFor (methodKey : MethodKey , arg : Tree )(f : => Tree ): Tree = {
13841393 val decodeMethodName = decodeMethodNames.getOrElse(methodKey, {
13851394 val name = TermName (s " d ${decodeMethodNames.size}" )
13861395 val mtpe = methodKey.tpe
13871396 decodeMethodNames.update(methodKey, name)
1388- decodeMethodTrees +=
1397+ methodTrees +=
13891398 q " private[this] def $name(in: _root_.com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, default: $mtpe): $mtpe = $f"
13901399 name
13911400 })
13921401 q " $decodeMethodName(in, $arg) "
13931402 }
13941403
13951404 val encodeMethodNames = new mutable.HashMap [MethodKey , TermName ]
1396- val encodeMethodTrees = new mutable.ArrayBuffer [Tree ]
13971405
13981406 def withEncoderFor (methodKey : MethodKey , arg : Tree )(f : => Tree ): Tree = {
13991407 val encodeMethodName = encodeMethodNames.getOrElse(methodKey, {
14001408 val name = TermName (s " e ${encodeMethodNames.size}" )
14011409 encodeMethodNames.update(methodKey, name)
1402- encodeMethodTrees +=
1410+ methodTrees +=
14031411 q " private[this] def $name(x: ${methodKey.tpe}, out: _root_.com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): _root_.scala.Unit = $f"
14041412 name
14051413 })
@@ -1458,7 +1466,7 @@ object JsonCodecMaker {
14581466 case _ => cannotFindValueCodecError(tpe)
14591467 }
14601468 } else if (tpe.typeSymbol.isModuleClass) q " ${tpe.typeSymbol.asClass.module}"
1461- else if (tpe <:< typeOf[ AnyRef ] ) q " null "
1469+ else if (tpe <:< definitions. AnyRefTpe ) q " null "
14621470 else q " null.asInstanceOf[ $tpe] "
14631471 }
14641472
@@ -1904,7 +1912,7 @@ object JsonCodecMaker {
19041912 q """ if (in.isNextToken('"')) {
19051913 in.rollbackToken()
19061914 val l = in.readStringAsCharBuf()
1907- ${genReadEnumValue(javaEnumValues (tpe), q " in.enumValueError(l) " )}
1915+ ${genReadEnumValue(getJavaEnumInfo (tpe), q " in.enumValueError(l) " )}
19081916 } else in.readNullOrTokenError(default, '"') """
19091917 } else if (isTuple(tpe)) withDecoderFor(methodKey, default) {
19101918 val indexedTypes = typeArgs(tpe)
@@ -2271,15 +2279,14 @@ object JsonCodecMaker {
22712279 else q " out.writeVal(x.id) "
22722280 } else q " out.writeVal(x.toString) "
22732281 } else if (isJavaEnum(tpe)) withEncoderFor(methodKey, m) {
2274- val es = javaEnumValues(tpe)
2275- val encodingRequired = es.exists(e => isEncodingRequired(e.name))
2276- if (es.exists(_.transformed)) {
2277- val cases = es.map(e => cq " ${e.value} => ${e.name}" ) :+
2282+ val enumInfo = getJavaEnumInfo(tpe)
2283+ if (enumInfo.hasTransformed) {
2284+ val cases = enumInfo.valueInfos.map(e => cq " ${e.value} => ${e.name}" ) :+
22782285 cq """ _ => out.encodeError("illegal enum value: " + x) """
2279- if (encodingRequired ) q " out.writeVal(x match { case .. $cases }) "
2286+ if (enumInfo.doEncoding ) q " out.writeVal(x match { case .. $cases }) "
22802287 else q " out.writeNonEscapedAsciiVal(x match { case .. $cases }) "
22812288 } else {
2282- if (encodingRequired ) q " out.writeVal(x.name) "
2289+ if (enumInfo.doEncoding ) q " out.writeVal(x.name) "
22832290 else q " out.writeNonEscapedAsciiVal(x.name) "
22842291 }
22852292 } else if (isTuple(tpe)) withEncoderFor(methodKey, m) {
@@ -2354,8 +2361,7 @@ object JsonCodecMaker {
23542361 if (cfg.decodingOnly) q " _root_.scala.Predef.??? "
23552362 else genWriteVal(q " x " , types, cfg.isStringified, EmptyTree )
23562363 }
2357- .. $decodeMethodTrees
2358- .. $encodeMethodTrees
2364+ .. $methodTrees
23592365 .. ${fields.values.map(_._2)}
23602366 .. ${equalsMethods.values.map(_._2)}
23612367 .. ${nullValues.values.map(_._2)}
0 commit comments