 			output.writeUTFBytes("00000000".substring(0, 8 - hexString.length))
 			output.writeUTFBytes(hexString)
 		}
+		private static const allEnumValues:Dictionary = new Dictionary
 		private static function printEnum(output:IDataOutput,
 				value:int, enumType:Class):void {
-			const enumTypeDescription:XML = describeType(enumType)
-			// Not enumTypeDescription.*.@name,
-			// because haXe will replace constant to variable, WTF!
-			for each(var name:String in enumTypeDescription.*.@name) {
-				if (enumType[name] === value) {
-					output.writeUTFBytes(name)
-					return
-				}
-			}
-			throw new IOError(value + " is invalid for " +
-					enumTypeDescription.@name)
+			var enumValues:Array
+			if (enumType in allEnumValues) {
+				enumValues = allEnumValues[enumType]
+			} else {
+				const enumTypeDescription:XML = describeType(enumType)
+				// Not enumTypeDescription.*.@name,
+				// because haXe will replace all constants to variables, WTF!
+				const xmlNames:XMLList = enumTypeDescription.*.@name
+				enumValues = []
+				for each(var name:String in xmlNames) {
+					enumValues[enumType[name]] = name
+				}
+				allEnumValues[enumType] = enumValues
+			}
+			if (value in enumValues) {
+				output.writeUTFBytes(enumValues[value])
+			} else {
+				throw new IOError(value + " is invalid for " +
+						enumTypeDescription.@name)
+			}
 		}
 		private static function printBytes(output:IDataOutput,
 				value:ByteArray):void {

	import flash.errors.IllegalOperationError;
	import flash.errors.IOError;
	import flash.utils.describeType
	import flash.utils.getDefinitionByName;
	import flash.utils.IDataInput
	import flash.utils.IDataOutput

				break
			}
		}
		private static const allMessageFields:Dictionary = new Dictionary
		private static function printMessageFields(output:IDataOutput,
				message:Message,
				printSetting:PrintSetting,
				currentIndent:String = ""):void {
			var isFirst:Boolean = true
			const type:Class = Object(message).constructor
			var messageFields:XMLList
			if (type in allMessageFields) {
				messageFields = allMessageFields[type]
			} else {
				// TODO
				const description:XML = describeType(type)
				messageFields = description.*.
					(0 == String(@type).search(
						/^com.netease.protobuf.fieldDescriptors::(Repeated)?FieldDescriptor\$/) &&
						BaseFieldDescriptor(type[@name]).name.
								search(/^\./) == -1// Not extension
					).@name
				allMessageFields[type] = messageFields
			}
			
			// Not description.constant,
			// because haXe will replace constant to variable, WTF!
			for each (var fieldDescriptorName:String in messageFields) {
				const fieldDescriptor:BaseFieldDescriptor =
						type[fieldDescriptorName]
				const shortName:String = fieldDescriptor.fullName.substring(
						fieldDescriptor.fullName.lastIndexOf('.') + 1)
				if (fieldDescriptor.type == Array) {
