package msgpack

import (
	
	
	

	
)

const (
	minInternedStringLen = 3
	maxDictLen           = math.MaxUint16
)

var internedStringExtID = int8(math.MinInt8)

func () {
	extTypes[internedStringExtID] = &extInfo{
		Type:    stringType,
		Decoder: decodeInternedStringExt,
	}
}

func ( *Decoder,  reflect.Value,  int) error {
	,  := .decodeInternedStringIndex()
	if  != nil {
		return 
	}

	,  := .internedStringAtIndex()
	if  != nil {
		return 
	}

	.SetString()
	return nil
}

//------------------------------------------------------------------------------

func ( *Encoder,  reflect.Value) error {
	if .IsNil() {
		return .EncodeNil()
	}

	 = .Elem()
	if .Kind() == reflect.String {
		return .encodeInternedString(.String(), true)
	}
	return .EncodeValue()
}

func ( *Encoder,  reflect.Value) error {
	return .encodeInternedString(.String(), true)
}

func ( *Encoder) ( string,  bool) error {
	// Interned string takes at least 3 bytes. Plain string 1 byte + string len.
	if len() >= minInternedStringLen {
		if ,  := .dict[];  {
			return .encodeInternedStringIndex()
		}

		if  && len(.dict) < maxDictLen {
			if .dict == nil {
				.dict = make(map[string]int)
			}
			 := len(.dict)
			.dict[] = 
		}
	}

	return .encodeNormalString()
}

func ( *Encoder) ( int) error {
	if  <= math.MaxUint8 {
		if  := .writeCode(msgpcode.FixExt1);  != nil {
			return 
		}
		return .write1(byte(internedStringExtID), uint8())
	}

	if  <= math.MaxUint16 {
		if  := .writeCode(msgpcode.FixExt2);  != nil {
			return 
		}
		return .write2(byte(internedStringExtID), uint16())
	}

	if uint64() <= math.MaxUint32 {
		if  := .writeCode(msgpcode.FixExt4);  != nil {
			return 
		}
		return .write4(byte(internedStringExtID), uint32())
	}

	return fmt.Errorf("msgpack: interned string index=%d is too large", )
}

//------------------------------------------------------------------------------

func ( *Decoder,  reflect.Value) error {
	,  := .decodeInternedString(true)
	if  == nil {
		.Set(reflect.ValueOf())
		return nil
	}
	if  != nil {
		if ,  := .(unexpectedCodeError); ! {
			return 
		}
	}

	if  := .s.UnreadByte();  != nil {
		return 
	}
	return decodeInterfaceValue(, )
}

func ( *Decoder,  reflect.Value) error {
	,  := .decodeInternedString(true)
	if  != nil {
		return 
	}

	.SetString()
	return nil
}

func ( *Decoder) ( bool) (string, error) {
	,  := .readCode()
	if  != nil {
		return "", 
	}

	if msgpcode.IsFixedString() {
		 := int( & msgpcode.FixedStrMask)
		return .decodeInternedStringWithLen(, )
	}

	switch  {
	case msgpcode.Nil:
		return "", nil
	case msgpcode.FixExt1, msgpcode.FixExt2, msgpcode.FixExt4:
		, ,  := .extHeader()
		if  != nil {
			return "", 
		}
		if  != internedStringExtID {
			 := fmt.Errorf("msgpack: got ext type=%d, wanted %d",
				, internedStringExtID)
			return "", 
		}

		,  := .decodeInternedStringIndex()
		if  != nil {
			return "", 
		}

		return .internedStringAtIndex()
	case msgpcode.Str8, msgpcode.Bin8:
		,  := .uint8()
		if  != nil {
			return "", 
		}
		return .decodeInternedStringWithLen(int(), )
	case msgpcode.Str16, msgpcode.Bin16:
		,  := .uint16()
		if  != nil {
			return "", 
		}
		return .decodeInternedStringWithLen(int(), )
	case msgpcode.Str32, msgpcode.Bin32:
		,  := .uint32()
		if  != nil {
			return "", 
		}
		return .decodeInternedStringWithLen(int(), )
	}

	return "", unexpectedCodeError{
		code: ,
		hint: "interned string",
	}
}

func ( *Decoder) ( int) (int, error) {
	switch  {
	case 1:
		,  := .uint8()
		if  != nil {
			return 0, 
		}
		return int(), nil
	case 2:
		,  := .uint16()
		if  != nil {
			return 0, 
		}
		return int(), nil
	case 4:
		,  := .uint32()
		if  != nil {
			return 0, 
		}
		return int(), nil
	}

	 := fmt.Errorf("msgpack: unsupported ext len=%d decoding interned string", )
	return 0, 
}

func ( *Decoder) ( int) (string, error) {
	if  >= len(.dict) {
		 := fmt.Errorf("msgpack: interned string at index=%d does not exist", )
		return "", 
	}
	return .dict[], nil
}

func ( *Decoder) ( int,  bool) (string, error) {
	if  <= 0 {
		return "", nil
	}

	,  := .stringWithLen()
	if  != nil {
		return "", 
	}

	if  && len() >= minInternedStringLen && len(.dict) < maxDictLen {
		.dict = append(.dict, )
	}

	return , nil
}