// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package sdp

import (
	
	
	
	
	
	
)

var (
	errSDPInvalidSyntax       = errors.New("sdp: invalid syntax")
	errSDPInvalidNumericValue = errors.New("sdp: invalid numeric value")
	errSDPInvalidValue        = errors.New("sdp: invalid value")
	errSDPInvalidPortValue    = errors.New("sdp: invalid port value")
	errSDPCacheInvalid        = errors.New("sdp: invalid cache")

	//nolint: gochecknoglobals
	unmarshalCachePool = sync.Pool{
		New: func() interface{} {
			return &unmarshalCache{}
		},
	}
)

// UnmarshalString is the primary function that deserializes the session description
// message and stores it inside of a structured SessionDescription object.
//
// The States Transition Table describes the computation flow between functions
// (namely s1, s2, s3, ...) for a parsing procedure that complies with the
// specifications laid out by the rfc4566#section-5 as well as by JavaScript
// Session Establishment Protocol draft. Links:
//
//	https://tools.ietf.org/html/rfc4566#section-5
//	https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24
//
// https://tools.ietf.org/html/rfc4566#section-5
// Session description
//
//	v=  (protocol version)
//	o=  (originator and session identifier)
//	s=  (session name)
//	i=* (session information)
//	u=* (URI of description)
//	e=* (email address)
//	p=* (phone number)
//	c=* (connection information -- not required if included in
//	     all media)
//	b=* (zero or more bandwidth information lines)
//	One or more time descriptions ("t=" and "r=" lines; see below)
//	z=* (time zone adjustments)
//	k=* (encryption key)
//	a=* (zero or more session attribute lines)
//	Zero or more media descriptions
//
// Time description
//
//	t=  (time the session is active)
//	r=* (zero or more repeat times)
//
// Media description, if present
//
//	m=  (media name and transport address)
//	i=* (media title)
//	c=* (connection information -- optional if included at
//	     session level)
//	b=* (zero or more bandwidth information lines)
//	k=* (encryption key)
//	a=* (zero or more media attribute lines)
//
// In order to generate the following state table and draw subsequent
// deterministic finite-state automota ("DFA") the following regex was used to
// derive the DFA:
//
//	vosi?u?e?p?c?b*(tr*)+z?k?a*(mi?c?b*k?a*)*
//
// possible place and state to exit:
//
//	**   * * *  ** * * * *
//	99   1 1 1  11 1 1 1 1
//	     3 1 1  26 5 5 4 4
//
// Please pay close attention to the `k`, and `a` parsing states. In the table
// below in order to distinguish between the states belonging to the media
// description as opposed to the session description, the states are marked
// with an asterisk ("a*", "k*").
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
// | STATES | a* | a*,k* | a  | a,k | b  | b,c | e | i  | m  | o | p | r,t | s | t | u  | v | z  |
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
// |   s1   |    |       |    |     |    |     |   |    |    |   |   |     |   |   |    | 2 |    |
// |   s2   |    |       |    |     |    |     |   |    |    | 3 |   |     |   |   |    |   |    |
// |   s3   |    |       |    |     |    |     |   |    |    |   |   |     | 4 |   |    |   |    |
// |   s4   |    |       |    |     |    |   5 | 6 |  7 |    |   | 8 |     |   | 9 | 10 |   |    |
// |   s5   |    |       |    |     |  5 |     |   |    |    |   |   |     |   | 9 |    |   |    |
// |   s6   |    |       |    |     |    |   5 |   |    |    |   | 8 |     |   | 9 |    |   |    |
// |   s7   |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 | 10 |   |    |
// |   s8   |    |       |    |     |    |   5 |   |    |    |   |   |     |   | 9 |    |   |    |
// |   s9   |    |       |    |  11 |    |     |   |    | 12 |   |   |   9 |   |   |    |   | 13 |
// |   s10  |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 |    |   |    |
// |   s11  |    |       | 11 |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
// |   s12  |    |    14 |    |     |    |  15 |   | 16 | 12 |   |   |     |   |   |    |   |    |
// |   s13  |    |       |    |  11 |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
// |   s14  | 14 |       |    |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
// |   s15  |    |    14 |    |     | 15 |     |   |    | 12 |   |   |     |   |   |    |   |    |
// |   s16  |    |    14 |    |     |    |  15 |   |    | 12 |   |   |     |   |   |    |   |    |
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+ .
func ( *SessionDescription) ( string) error {
	var  bool
	 := new(lexer)
	if .cache,  = unmarshalCachePool.Get().(*unmarshalCache); ! {
		return errSDPCacheInvalid
	}
	defer unmarshalCachePool.Put(.cache)

	.cache.reset()
	.desc = 
	.value = 

	for  := s1;  != nil; {
		var  error
		,  = ()
		if  != nil {
			return 
		}
	}

	.Attributes = .cache.cloneSessionAttributes()
	populateMediaAttributes(.cache, .desc)

	return nil
}

// Unmarshal converts the value into a []byte and then calls UnmarshalString.
// Callers should use the more performant UnmarshalString.
func ( *SessionDescription) ( []byte) error {
	return .UnmarshalString(string())
}

func s1( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		if  == 'v' {
			return unmarshalProtocolVersion
		}

		return nil
	})
}

func s2( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		if  == 'o' {
			return unmarshalOrigin
		}

		return nil
	})
}

func s3( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		if  == 's' {
			return unmarshalSessionName
		}

		return nil
	})
}

func s4( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'i':
			return unmarshalSessionInformation
		case 'u':
			return unmarshalURI
		case 'e':
			return unmarshalEmail
		case 'p':
			return unmarshalPhone
		case 'c':
			return unmarshalSessionConnectionInformation
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s5( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s6( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'p':
			return unmarshalPhone
		case 'c':
			return unmarshalSessionConnectionInformation
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s7( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'u':
			return unmarshalURI
		case 'e':
			return unmarshalEmail
		case 'p':
			return unmarshalPhone
		case 'c':
			return unmarshalSessionConnectionInformation
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s8( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'c':
			return unmarshalSessionConnectionInformation
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s9( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'z':
			return unmarshalTimeZones
		case 'k':
			return unmarshalSessionEncryptionKey
		case 'a':
			return unmarshalSessionAttribute
		case 'r':
			return unmarshalRepeatTimes
		case 't':
			return unmarshalTiming
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s10( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'e':
			return unmarshalEmail
		case 'p':
			return unmarshalPhone
		case 'c':
			return unmarshalSessionConnectionInformation
		case 'b':
			return unmarshalSessionBandwidth
		case 't':
			return unmarshalTiming
		}

		return nil
	})
}

func s11( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalSessionAttribute
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s12( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalMediaAttribute
		case 'k':
			return unmarshalMediaEncryptionKey
		case 'b':
			return unmarshalMediaBandwidth
		case 'c':
			return unmarshalMediaConnectionInformation
		case 'i':
			return unmarshalMediaTitle
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s13( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalSessionAttribute
		case 'k':
			return unmarshalSessionEncryptionKey
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s14( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalMediaAttribute
		case 'k':
			// Non-spec ordering
			return unmarshalMediaEncryptionKey
		case 'b':
			// Non-spec ordering
			return unmarshalMediaBandwidth
		case 'c':
			// Non-spec ordering
			return unmarshalMediaConnectionInformation
		case 'i':
			// Non-spec ordering
			return unmarshalMediaTitle
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s15( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalMediaAttribute
		case 'k':
			return unmarshalMediaEncryptionKey
		case 'b':
			return unmarshalMediaBandwidth
		case 'c':
			return unmarshalMediaConnectionInformation
		case 'i':
			// Non-spec ordering
			return unmarshalMediaTitle
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func s16( *lexer) (stateFn, error) {
	return .handleType(func( byte) stateFn {
		switch  {
		case 'a':
			return unmarshalMediaAttribute
		case 'k':
			return unmarshalMediaEncryptionKey
		case 'c':
			return unmarshalMediaConnectionInformation
		case 'b':
			return unmarshalMediaBandwidth
		case 'i':
			// Non-spec ordering
			return unmarshalMediaTitle
		case 'm':
			return unmarshalMediaDescription
		}

		return nil
	})
}

func unmarshalProtocolVersion( *lexer) (stateFn, error) {
	,  := .readUint64Field()
	if  != nil {
		return nil, 
	}

	// As off the latest draft of the rfc this value is required to be 0.
	// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-5.8.1
	if  != 0 {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}

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

	return s2, nil
}

func unmarshalOrigin( *lexer) (stateFn, error) {
	var  error

	.desc.Origin.Username,  = .readField()
	if  != nil {
		return nil, 
	}

	.desc.Origin.SessionID,  = .readUint64Field()
	if  != nil {
		return nil, 
	}

	.desc.Origin.SessionVersion,  = .readUint64Field()
	if  != nil {
		return nil, 
	}

	.desc.Origin.NetworkType,  = .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-8.2.6
	if !anyOf(.desc.Origin.NetworkType, "IN") {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, .desc.Origin.NetworkType)
	}

	.desc.Origin.AddressType,  = .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-8.2.7
	if !anyOf(.desc.Origin.AddressType, "IP4", "IP6") {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, .desc.Origin.AddressType)
	}

	.desc.Origin.UnicastAddress,  = .readField()
	if  != nil {
		return nil, 
	}

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

	return s3, nil
}

func unmarshalSessionName( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	.desc.SessionName = SessionName()

	return s4, nil
}

func unmarshalSessionInformation( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := Information()
	.desc.SessionInformation = &

	return s7, nil
}

func unmarshalURI( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	.desc.URI,  = url.Parse()
	if  != nil {
		return nil, 
	}

	return s10, nil
}

func unmarshalEmail( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := EmailAddress()
	.desc.EmailAddress = &

	return s6, nil
}

func unmarshalPhone( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := PhoneNumber()
	.desc.PhoneNumber = &

	return s8, nil
}

func unmarshalSessionConnectionInformation( *lexer) (stateFn, error) {
	var  error
	.desc.ConnectionInformation,  = .unmarshalConnectionInformation()
	if  != nil {
		return nil, 
	}

	return s5, nil
}

func ( *lexer) () (*ConnectionInformation, error) {
	var  error
	var  ConnectionInformation

	.NetworkType,  = .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-8.2.6
	if !anyOf(.NetworkType, "IN") {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, .NetworkType)
	}

	.AddressType,  = .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-8.2.7
	if !anyOf(.AddressType, "IP4", "IP6") {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, .AddressType)
	}

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

	if  != "" {
		.Address = new(Address)
		.Address.Address = 
	}

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

	return &, nil
}

func unmarshalSessionBandwidth( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	,  := unmarshalBandwidth()
	if  != nil {
		return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, )
	}
	.desc.Bandwidth = append(.desc.Bandwidth, *)

	return s5, nil
}

func unmarshalBandwidth( string) (*Bandwidth, error) {
	 := strings.Split(, ":")
	if len() != 2 {
		return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, )
	}

	 := strings.HasPrefix([0], "X-")
	if  {
		[0] = strings.TrimPrefix([0], "X-")
	} else if !anyOf([0], "CT", "AS", "TIAS", "RS", "RR") {
		// Set according to currently registered with IANA
		// https://tools.ietf.org/html/rfc4566#section-5.8
		// https://tools.ietf.org/html/rfc3890#section-6.2
		// https://tools.ietf.org/html/rfc3556#section-2
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, [0])
	}

	,  := strconv.ParseUint([1], 10, 64)
	if  != nil {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, [1])
	}

	return &Bandwidth{
		Experimental: ,
		Type:         [0],
		Bandwidth:    ,
	}, nil
}

func unmarshalTiming( *lexer) (stateFn, error) {
	var  error
	var  TimeDescription

	.Timing.StartTime,  = .readUint64Field()
	if  != nil {
		return nil, 
	}

	.Timing.StopTime,  = .readUint64Field()
	if  != nil {
		return nil, 
	}

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

	.desc.TimeDescriptions = append(.desc.TimeDescriptions, )

	return s9, nil
}

func unmarshalRepeatTimes( *lexer) (stateFn, error) {
	var  error
	var  RepeatTime

	 := &.desc.TimeDescriptions[len(.desc.TimeDescriptions)-1]

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

	.Interval,  = parseTimeUnits()
	if  != nil {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}

	,  = .readField()
	if  != nil {
		return nil, 
	}

	.Duration,  = parseTimeUnits()
	if  != nil {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}

	for {
		,  := .readField()
		if  != nil {
			return nil, 
		}
		if  == "" {
			break
		}
		,  := parseTimeUnits()
		if  != nil {
			return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
		}
		.Offsets = append(.Offsets, )
	}

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

	.RepeatTimes = append(.RepeatTimes, )

	return s9, nil
}

func unmarshalTimeZones( *lexer) (stateFn, error) {
	// These fields are transimitted in pairs
	// z=<adjustment time> <offset> <adjustment time> <offset> ....
	// so we are making sure that there are actually multiple of 2 total.
	for {
		var  error
		var  TimeZone

		.AdjustmentTime,  = .readUint64Field()
		if  != nil {
			return nil, 
		}

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

		if  == "" {
			break
		}

		.Offset,  = parseTimeUnits()
		if  != nil {
			return nil, 
		}

		.desc.TimeZones = append(.desc.TimeZones, )
	}

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

	return s13, nil
}

func unmarshalSessionEncryptionKey( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := EncryptionKey()
	.desc.EncryptionKey = &

	return s11, nil
}

func unmarshalSessionAttribute( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := strings.IndexRune(, ':')
	 := .cache.getSessionAttribute()
	if  > 0 {
		.Key = [:]
		.Value = [+1:]
	} else {
		.Key = 
	}

	return s11, nil
}

func unmarshalMediaDescription( *lexer) (stateFn, error) { //nolint:cyclop
	populateMediaAttributes(.cache, .desc)
	var  MediaDescription

	// <media>
	,  := .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-5.14
	if !anyOf(, "audio", "video", "text", "application", "message") {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}
	.MediaName.Media = 

	// <port>
	,  = .readField()
	if  != nil {
		return nil, 
	}
	 := strings.Split(, "/")
	.MediaName.Port.Value,  = parsePort([0])
	if  != nil {
		return nil, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, [0])
	}

	if len() > 1 {
		var  int
		,  = strconv.Atoi([1])
		if  != nil {
			return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
		}
		.MediaName.Port.Range = &
	}

	// <proto>
	,  = .readField()
	if  != nil {
		return nil, 
	}

	// Set according to currently registered with IANA
	// https://tools.ietf.org/html/rfc4566#section-5.14
	// https://tools.ietf.org/html/rfc4975#section-8.1
	for ,  := range strings.Split(, "/") {
		if !anyOf(
			,
			"UDP",
			"RTP",
			"AVP",
			"SAVP",
			"SAVPF",
			"TLS",
			"DTLS",
			"SCTP",
			"AVPF",
			"TCP",
			"MSRP",
			"BFCP",
			"UDT",
			"IX",
			"MRCPv2",
		) {
			return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, )
		}
		.MediaName.Protos = append(.MediaName.Protos, )
	}

	// <fmt>...
	for {
		,  = .readField()
		if  != nil {
			return nil, 
		}
		if  == "" {
			break
		}
		.MediaName.Formats = append(.MediaName.Formats, )
	}

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

	.desc.MediaDescriptions = append(.desc.MediaDescriptions, &)

	return s12, nil
}

func unmarshalMediaTitle( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := .desc.MediaDescriptions[len(.desc.MediaDescriptions)-1]
	 := Information()
	.MediaTitle = &

	return s16, nil
}

func unmarshalMediaConnectionInformation( *lexer) (stateFn, error) {
	var  error
	 := .desc.MediaDescriptions[len(.desc.MediaDescriptions)-1]
	.ConnectionInformation,  = .unmarshalConnectionInformation()
	if  != nil {
		return nil, 
	}

	return s15, nil
}

func unmarshalMediaBandwidth( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := .desc.MediaDescriptions[len(.desc.MediaDescriptions)-1]
	,  := unmarshalBandwidth()
	if  != nil {
		return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidSyntax, )
	}
	.Bandwidth = append(.Bandwidth, *)

	return s15, nil
}

func unmarshalMediaEncryptionKey( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := .desc.MediaDescriptions[len(.desc.MediaDescriptions)-1]
	 := EncryptionKey()
	.EncryptionKey = &

	return s14, nil
}

func unmarshalMediaAttribute( *lexer) (stateFn, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}

	 := strings.IndexRune(, ':')
	 := .cache.getMediaAttribute()
	if  > 0 {
		.Key = [:]
		.Value = [+1:]
	} else {
		.Key = 
	}

	return s14, nil
}

func parseTimeUnits( string) ( int64,  error) {
	if len() == 0 {
		return 0, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}
	 := timeShorthand([len()-1])
	if  > 0 {
		,  = strconv.ParseInt([:len()-1], 10, 64)
	} else {
		 = 1
		,  = strconv.ParseInt(, 10, 64)
	}
	if  != nil {
		return 0, fmt.Errorf("%w `%v`", errSDPInvalidValue, )
	}

	return  * , nil
}

func timeShorthand( byte) int64 {
	// Some time offsets in the protocol can be provided with a shorthand
	// notation. This code ensures to convert it to NTP timestamp format.
	switch  {
	case 'd': // days
		return 86400
	case 'h': // hours
		return 3600
	case 'm': // minutes
		return 60
	case 's': // seconds (allowed for completeness)
		return 1
	default:
		return 0
	}
}

func parsePort( string) (int, error) {
	,  := strconv.Atoi()
	if  != nil {
		return 0, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, )
	}

	if  < 0 ||  > 65535 {
		return 0, fmt.Errorf("%w -- out of range `%v`", errSDPInvalidPortValue, )
	}

	return , nil
}

func populateMediaAttributes( *unmarshalCache,  *SessionDescription) {
	if len(.MediaDescriptions) != 0 {
		 := .MediaDescriptions[len(.MediaDescriptions)-1]
		.Attributes = .cloneMediaAttributes()
	}
}