Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,4 @@ trait IDNAException extends RuntimeException
object IDNAException {
implicit val showForIDNAException: Show[IDNAException] =
Show.fromToString

/**
* Exception used both in Bootstring and UTS-46 and which I doubt will ever be thrown.
*/
final private[idna4s] class UnableToResizeBufferException extends IDNAException {
override val getMessage: String =
s"Can not resize buffer as it would exceed largest valid size ${Int.MaxValue}. What are you doing?"

final override def toString: String =
s"UnableToResizeBufferException(${getMessage})"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,64 @@ import scala.util.control.NonFatal

object Bootstring {

private val HalfIntMaxValue: Int = Int.MaxValue / 2

/**
* Calculate the a new size for an `IntBuffer` so that it can accept at ''least'' the given
* new capacity.
*
* If the buffer is already at or exceeding the required size, then the buffer's current size
* is returned. Otherwise attempt to double the buffer's size as long as that won't overflow.
* If we can not double it, add `neededSize - remaining` to the current capacity. In the
* unbelievable case where `buffer.remaining + neededSize > Int.MaxValue`, then yield an
* error.
*/
@inline
private def calculateNewSize(buffer: IntBuffer, neededSize: Int): Int =
if (buffer.remaining >= neededSize) {
// This will be the branch most often hit by a wide margin.
buffer.capacity
} else if (buffer.capacity <= HalfIntMaxValue && buffer.capacity + buffer.remaining >= neededSize) {
// Double it
buffer.capacity * 2
} else if (neededSize.toLong - buffer.remaining.toLong <= Int.MaxValue.toLong) {
// I do not expect this branch will ever be executed under normal
// circumstances.
neededSize - buffer.remaining
} else {
// I do not expect this branch will ever be executed under normal
// circumstances.
throw BootstringException.UnableToResizeBufferException
}

/**
* Copy the contents of a given `IntBuffer` into a new `IntBuffer` with double capacity if the
* given `IntBuffer` is at capacity, unless doubling it would overflow, in that case attempt
* to just add the minimum needed allocation, if that is not possible then throw an error.
*
* The error case should only happen if there is a bug or someone is intentionally abusing the
* system. We need to handle it as it could be used to influence the result to potentially
* change a URI.
*/
@inline
private def maybeResize(buffer: IntBuffer, neededSize: Int): IntBuffer =
if (buffer.remaining >= neededSize) {
// This will be the branch most often hit by a wide margin.
buffer
} else {
val pos: Int = buffer.position
val newSize: Int = calculateNewSize(buffer, neededSize)

// Shadow here is because `(buffer: IntBuffer).position(pos): Buffer`
// but we want `IntBuffer`, e.g. it is getting widened to the super
// type.
IntBuffer.allocate(newSize).put(buffer.array) match {
case buffer =>
buffer.position(pos)
buffer
}
}

/**
* Bootstring encode given `String`.
*
Expand Down
66 changes: 2 additions & 64 deletions core/shared/src/main/scala/org/typelevel/idna4s/core/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,12 @@ package object core {
acc :+ value
}

private[idna4s] def codePointsAsBuffer(value: String): IntBuffer = {
val result: IntBuffer = foldLeftCodePoints(value)(IntBuffer.allocate(value.length)) {
private[idna4s] def codePointsAsBuffer(value: String): IntBuffer =
foldLeftCodePoints(value)(IntBuffer.allocate(value.length)) {
case (acc, value) =>
acc.put(value)
}

result.flip
result
}

private[idna4s] def stringFromCodePoints(value: IntBuffer): Either[String, String] = {
val out: CharBuffer = CharBuffer.allocate(value.remaining * 2)

Expand Down Expand Up @@ -107,62 +103,4 @@ package object core {

buffer
}

final private[this] val HalfIntMaxValue = Int.MaxValue / 2

/**
* Calculate the a new size for an `IntBuffer` so that it can accept at ''least'' the given
* new capacity.
*
* If the buffer is already at or exceeding the required size, then the buffer's current size
* is returned. Otherwise attempt to double the buffer's size as long as that won't overflow.
* If we can not double it, add `neededSize - remaining` to the current capacity. In the
* unbelievable case where `buffer.remaining + neededSize > Int.MaxValue`, then yield an
* error.
*/
@inline
private def calculateNewSize(buffer: IntBuffer, neededSize: Int): Int =
if (buffer.remaining >= neededSize) {
// This will be the branch most often hit by a wide margin.
buffer.capacity
} else if (buffer.capacity <= HalfIntMaxValue && buffer.capacity + buffer.remaining >= neededSize) {
// Double it
buffer.capacity * 2
} else if (neededSize.toLong - buffer.remaining.toLong <= Int.MaxValue.toLong) {
// I do not expect this branch will ever be executed under normal
// circumstances.
neededSize - buffer.remaining
} else {
// I do not expect this branch will ever be executed under normal
// circumstances.
throw new IDNAException.UnableToResizeBufferException
}

/**
* Copy the contents of a given `IntBuffer` into a new `IntBuffer` with double capacity if the
* given `IntBuffer` is at capacity, unless doubling it would overflow, in that case attempt
* to just add the minimum needed allocation, if that is not possible then throw an error.
*
* The error case should only happen if there is a bug or someone is intentionally abusing the
* system. We need to handle it as it could be used to influence the result to potentially
* change a URI.
*/
@inline
private[idna4s] def maybeResize(buffer: IntBuffer, neededSize: Int): IntBuffer =
if (buffer.remaining >= neededSize) {
// This will be the branch most often hit by a wide margin.
buffer
} else {
val pos: Int = buffer.position
val newSize: Int = calculateNewSize(buffer, neededSize)

// Shadow here is because `(buffer: IntBuffer).position(pos): Buffer`
// but we want `IntBuffer`, e.g. it is getting widened to the super
// type.
IntBuffer.allocate(newSize).put(buffer.array) match {
case buffer =>
buffer.position(pos)
buffer
}
}
}
Loading