import * as tsplus_module_1 from "@tsplus/stdlib/collections/Chunk/empty";
import * as tsplus_module_2 from "@effect/core/stream/Channel/operations/pipeTo";
import * as tsplus_module_3 from "@effect/core/stream/Channel/operations/suspend";
import * as tsplus_module_4 from "@effect/core/stream/Channel/operations/write";
import * as tsplus_module_5 from "@effect/core/stream/Channel/operations/zipRight";
import * as tsplus_module_6 from "@effect/core/stream/Channel/operations/fail";
import * as tsplus_module_7 from "@tsplus/stdlib/collections/Chunk/isEmpty";
import * as tsplus_module_8 from "@effect/core/stream/Channel/operations/unit";
import * as tsplus_module_9 from "@effect/core/stream/Channel/operations/readWith";
import * as tsplus_module_10 from "@tsplus/stdlib/collections/Chunk/concat";
import * as tsplus_module_11 from "@tsplus/stdlib/collections/Chunk/splitAt";
import * as tsplus_module_12 from "@tsplus/stdlib/collections/Chunk/materialize";
import * as tsplus_module_13 from "@tsplus/stdlib/data/Tuple/make";
import * as tsplus_module_14 from "@tsplus/stdlib/collections/Chunk/single";
import * as tsplus_module_15 from "@tsplus/stdlib/collections/Chunk/unsafeGet";
import * as tsplus_module_16 from "@tsplus/stdlib/collections/List/from";
import * as tsplus_module_17 from "@tsplus/stdlib/collections/List/find";
import * as tsplus_module_18 from "@tsplus/stdlib/data/Maybe/definition";
import { concreteStream, StreamInternal } from "@effect/core/stream/Stream/operations/_internal/StreamInternal";
const emptyByteChunk = /*#__PURE__*/tsplus_module_1.empty();
const emptyStringChunk = /*#__PURE__*/tsplus_module_1.empty();
export function utf8DecodeNoBom(stream, __tsplusTrace) {
  void 0;
  return new StreamInternal(tsplus_module_3.suspend(() => tsplus_module_2.pipeTo_(stream.channel, () => readThenTransduce(emptyByteChunk, __tsplusTrace))));
}

function readThenTransduce(buffer, __tsplusTrace) {
  return tsplus_module_9.readWith(received => {
    const {
      tuple: [string, buffered]
    } = process(buffer, received);
    return tsplus_module_5.zipRight_(tsplus_module_4.write(() => string), () => readThenTransduce(buffered, __tsplusTrace));
  }, err => tsplus_module_6.fail(() => err), () => tsplus_module_7.isEmpty(buffer) ? tsplus_module_8.unit : tsplus_module_4.write(() => stringChunkFrom(buffer)));
}

function process(buffered, received) {
  const bytes = tsplus_module_10.concat_(buffered, received);
  const {
    tuple: [chunk, rest]
  } = tsplus_module_11.splitAt_(bytes, computeSplitIndex(bytes));

  if (tsplus_module_7.isEmpty(chunk)) {
    return tsplus_module_13.make(emptyStringChunk, tsplus_module_12.materialize(rest));
  }

  if (tsplus_module_7.isEmpty(rest)) {
    return tsplus_module_13.make(stringChunkFrom(chunk), emptyByteChunk);
  }

  return tsplus_module_13.make(stringChunkFrom(chunk), rest);
}

function stringChunkFrom(bytes) {
  return tsplus_module_14.single(String.fromCharCode(...bytes));
}

function computeSplitIndex(chunk) {
  // There are 3 bad patterns we need to check to detect an incomplete chunk:
  // - 2/3/4 byte sequences that start on the last byte
  // - 3/4 byte sequences that start on the second-to-last byte
  // - 4 byte sequences that start on the third-to-last byte
  //
  // Otherwise, we can convert the entire concatenated chunk to a string.
  const size = chunk.length;

  if (size >= 1 && tsplus_module_18.isSome(tsplus_module_17.find_(tsplus_module_16.make(is2ByteStart, is3ByteStart, is4ByteStart), f => f(tsplus_module_15.unsafeGet_(chunk, size - 1))))) {
    return size - 1;
  }

  if (size >= 2 && tsplus_module_18.isSome(tsplus_module_17.find_(tsplus_module_16.make(is3ByteStart, is4ByteStart), f => f(tsplus_module_15.unsafeGet_(chunk, size - 2))))) {
    return size - 2;
  }

  if (size >= 3 && is4ByteStart(tsplus_module_15.unsafeGet_(chunk, size - 3))) {
    return size - 3;
  }

  return size;
}

function is2ByteStart(byte) {
  return (byte & 0xe0) === 0xc0;
}

function is3ByteStart(byte) {
  return (byte & 0xf0) === 0xe0;
}

function is4ByteStart(byte) {
  return (byte & 0xf8) === 0xf0;
}
//# sourceMappingURL=utf8DecodeNoBom.mjs.map