  void Compute(OpKernelContext* ctx) override {
    const Tensor& shape_tensor = ctx->input(0);
    const Tensor& means_tensor = ctx->input(1);
    const Tensor& stddevs_tensor = ctx->input(2);
    const Tensor& minvals_tensor = ctx->input(3);
    const Tensor& maxvals_tensor = ctx->input(4);

    OP_REQUIRES(
        ctx, TensorShapeUtils::IsVector(shape_tensor.shape()),
        errors::InvalidArgument("Input shape should be a vector, got shape: ",
                                shape_tensor.shape().DebugString()));
    int32 num_batches = shape_tensor.flat<int32>()(0);

    int32 samples_per_batch = 1;
    const int32 num_dims = shape_tensor.dim_size(0);
    for (int32 i = 1; i < num_dims; i++) {
      samples_per_batch *= shape_tensor.flat<int32>()(i);
    }
    const int32 num_elements = num_batches * samples_per_batch;

    // Allocate the output before fudging num_batches and samples_per_batch.
    auto shape_vec = shape_tensor.flat<int32>();
    TensorShape tensor_shape;
    OP_REQUIRES_OK(ctx, TensorShapeUtils::MakeShape(
                            shape_vec.data(), shape_vec.size(), &tensor_shape));
    Tensor* samples_tensor;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, tensor_shape, &samples_tensor));

    // Parameters must be 0-d or 1-d.
    OP_REQUIRES(ctx, means_tensor.dims() <= 1,
                errors::InvalidArgument(
                    "Input means should be a scalar or vector, got shape: ",
                    means_tensor.shape().DebugString()));
    OP_REQUIRES(ctx, stddevs_tensor.dims() <= 1,
                errors::InvalidArgument(
                    "Input stddevs should be a scalar or vector, got shape: ",
                    stddevs_tensor.shape().DebugString()));
    OP_REQUIRES(ctx, minvals_tensor.dims() <= 1,
                errors::InvalidArgument(
                    "Input minvals should be a scalar or vector, got shape: ",
                    minvals_tensor.shape().DebugString()));
    OP_REQUIRES(ctx, maxvals_tensor.dims() <= 1,
                errors::InvalidArgument(
                    "Input maxvals should be a scalar or vector, got shape: ",
                    maxvals_tensor.shape().DebugString()));

    if ((means_tensor.dims() == 0 || means_tensor.dim_size(0) == 1) &&
        (stddevs_tensor.dims() == 0 || stddevs_tensor.dim_size(0) == 1) &&
        minvals_tensor.dims() == 0 && maxvals_tensor.dims() == 0) {
      // All batches have the same parameters, so we can update the batch size
      // to a reasonable value to improve parallelism (ensure enough batches,
      // and no very small batches which have high overhead).
      int32 size = num_batches * samples_per_batch;
      int32 adjusted_samples = kDesiredBatchSize;
      // Ensure adjusted_batches * adjusted_samples >= size.
      int32 adjusted_batches = Eigen::divup(size, adjusted_samples);
      num_batches = adjusted_batches;
      samples_per_batch = adjusted_samples;
    } else {
      // Parameters must be broadcastable to the shape [num_batches].
      OP_REQUIRES(
          ctx,
          TensorShapeUtils::IsScalar(means_tensor.shape()) ||
              means_tensor.dim_size(0) == 1 ||
              means_tensor.dim_size(0) == num_batches,
          errors::InvalidArgument(
              "Input means should have length 1 or shape[0], got shape: ",
              means_tensor.shape().DebugString()));
      OP_REQUIRES(
          ctx,
          TensorShapeUtils::IsScalar(stddevs_tensor.shape()) ||
              stddevs_tensor.dim_size(0) == 1 ||
              stddevs_tensor.dim_size(0) == num_batches,
          errors::InvalidArgument(
              "Input stddevs should have length 1 or shape[0], got shape: ",
              stddevs_tensor.shape().DebugString()));
      OP_REQUIRES(
          ctx,
          TensorShapeUtils::IsScalar(minvals_tensor.shape()) ||
              minvals_tensor.dim_size(0) == 1 ||
              minvals_tensor.dim_size(0) == num_batches,
          errors::InvalidArgument(
              "Input minvals should have length 1 or shape[0], got shape: ",
              minvals_tensor.shape().DebugString()));
      OP_REQUIRES(
          ctx,
          TensorShapeUtils::IsScalar(maxvals_tensor.shape()) ||
              maxvals_tensor.dim_size(0) == 1 ||
              maxvals_tensor.dim_size(0) == num_batches,
          errors::InvalidArgument(
              "Input maxvals should have length 1 or shape[0], got shape: ",
              maxvals_tensor.shape().DebugString()));
    }

    auto truncFunctor = functor::TruncatedNormalFunctor<Device, T>();
    // Each worker has the fudge factor for samples_per_batch, so use it here.
    random::PhiloxRandom rng =
        generator_.ReserveSamples128(num_batches * 2 * functor::kMaxIterations *
                                     (samples_per_batch + 3) / 4);
    truncFunctor(ctx, ctx->eigen_device<Device>(), num_batches,
                 samples_per_batch, num_elements, means_tensor.flat<T>(),
                 stddevs_tensor.flat<T>(), minvals_tensor.flat<T>(),
                 maxvals_tensor.flat<T>(), rng, samples_tensor->flat<T>());
  }

Fix breakage in parameterized_truncated_normal_op.cc

PiperOrigin-RevId: 372041718
Change-Id: Iff79e77a2bb27032423eefcb84211627b27dfe81


static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
{
#ifndef PB_ENABLE_MALLOC
    PB_UNUSED(wire_type);
    PB_UNUSED(iter);
    PB_RETURN_ERROR(stream, "no malloc support");
#else
    pb_type_t type;
    pb_decoder_t func;

    type = iter->pos->type;
    func = PB_DECODERS[PB_LTYPE(type)];

    switch (PB_HTYPE(type))
    {
        case PB_HTYPE_REQUIRED:
        case PB_HTYPE_OPTIONAL:
        case PB_HTYPE_ONEOF:
            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
                *(void**)iter->pData != NULL)
            {
                /* Duplicate field, have to release the old allocation first. */
                pb_release_single_field(iter);
            }

            if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
            {
                *(pb_size_t*)iter->pSize = iter->pos->tag;
            }

            if (PB_LTYPE(type) == PB_LTYPE_STRING ||
                PB_LTYPE(type) == PB_LTYPE_BYTES)
            {
                return func(stream, iter->pos, iter->pData);
            }
            else
            {
                if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1))
                    return false;

                initialize_pointer_field(*(void**)iter->pData, iter);
                return func(stream, iter->pos, *(void**)iter->pData);
            }

        case PB_HTYPE_REPEATED:
            if (wire_type == PB_WT_STRING
                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
            {
                /* Packed array, multiple items come in at once. */
                bool status = true;
                pb_size_t *size = (pb_size_t*)iter->pSize;
                size_t allocated_size = *size;
                void *pItem;
                pb_istream_t substream;

                if (!pb_make_string_substream(stream, &substream))
                    return false;

                while (substream.bytes_left)
                {
                    if ((size_t)*size + 1 > allocated_size)
                    {
                        /* Allocate more storage. This tries to guess the
                         * number of remaining entries. Round the division
                         * upwards. */
                        allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;

                        if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
                        {
                            status = false;
                            break;
                        }
                    }

                    /* Decode the array entry */
                    pItem = *(char**)iter->pData + iter->pos->data_size * (*size);
                    initialize_pointer_field(pItem, iter);
                    if (!func(&substream, iter->pos, pItem))
                    {
                        status = false;
                        break;
                    }

                    if (*size == PB_SIZE_MAX)
                    {
#ifndef PB_NO_ERRMSG
                        stream->errmsg = "too many array entries";
#endif
                        status = false;
                        break;
                    }

                    (*size)++;
                }
                if (!pb_close_string_substream(stream, &substream))
                    return false;

                return status;
            }
            else
            {
                /* Normal repeated field, i.e. only one item at a time. */
                pb_size_t *size = (pb_size_t*)iter->pSize;
                void *pItem;

                if (*size == PB_SIZE_MAX)
                    PB_RETURN_ERROR(stream, "too many array entries");

                (*size)++;
                if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
                    return false;

                pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1);
                initialize_pointer_field(pItem, iter);
                return func(stream, iter->pos, pItem);
            }

        default:
            PB_RETURN_ERROR(stream, "invalid field type");
    }
#endif
}

Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p)

#define checkreturn

#define checkreturn __attribute__((warn_unused_result))

typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest)

static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);

static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);

static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);

static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);

static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);

static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension);

static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);

static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);

static bool checkreturn

static void pb_field_set_to_default(pb_field_iter_t *iter);

static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);

static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);

static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size);

static void pb_release_single_field(const pb_field_iter_t *iter);

static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
    &pb_dec_bool,
    &pb_dec_varint,
    &pb_dec_uvarint,
    &pb_dec_svarint,
    &pb_dec_fixed32,
    &pb_dec_fixed64,

    &pb_dec_bytes,
    &pb_dec_string,
    &pb_dec_submessage,
    NULL, /* extensions */
    &pb_dec_fixed_length_bytes
};


  void Compute(OpKernelContext* context) override {
    // Here's the basic idea:
    // Batch and depth dimension are independent from row and col dimension. And
    // because FractionalAvgPool currently only support pooling along row and
    // col, we can basically think of this 4D tensor backpropagation as
    // operation of a series of 2D planes.
    //
    // For each element of a 'slice' (2D plane) of output_backprop, we need to
    // figure out its contributors when doing FractionalAvgPool operation. This
    // can be done based on row_pooling_sequence, col_pooling_seq and
    // overlapping.
    // Once we figure out the original contributors, we just need to evenly
    // divide the value of this element among these contributors.
    //
    // Internally, we divide the out_backprop tensor and store it in a temporary
    // tensor of double type. And cast it to the corresponding type.
    typedef Eigen::Map<const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>
        ConstEigenMatrixMap;
    typedef Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>>
        EigenDoubleMatrixMap;

    // Grab the inputs.
    const Tensor& orig_input_tensor_shape = context->input(0);
    OP_REQUIRES(context,
                orig_input_tensor_shape.dims() == 1 &&
                    orig_input_tensor_shape.NumElements() == 4,
                errors::InvalidArgument("original input tensor shape must be"
                                        "1-dimensional and 4 elements"));
    const Tensor& out_backprop = context->input(1);
    const Tensor& row_seq_tensor = context->input(2);
    const Tensor& col_seq_tensor = context->input(3);

    const int64 out_batch = out_backprop.dim_size(0);
    const int64 out_rows = out_backprop.dim_size(1);
    const int64 out_cols = out_backprop.dim_size(2);
    const int64 out_depth = out_backprop.dim_size(3);

    auto row_seq_tensor_flat = row_seq_tensor.flat<int64>();
    auto col_seq_tensor_flat = col_seq_tensor.flat<int64>();
    auto orig_input_tensor_shape_flat = orig_input_tensor_shape.flat<int64>();

    const int64 in_batch = orig_input_tensor_shape_flat(0);
    const int64 in_rows = orig_input_tensor_shape_flat(1);
    const int64 in_cols = orig_input_tensor_shape_flat(2);
    const int64 in_depth = orig_input_tensor_shape_flat(3);

    constexpr int tensor_in_and_out_dims = 4;
    // Transform orig_input_tensor_shape into TensorShape
    TensorShape in_shape;
    for (auto i = 0; i < tensor_in_and_out_dims; ++i) {
      in_shape.AddDim(orig_input_tensor_shape_flat(i));
    }

    // Create intermediate in_backprop.
    Tensor in_backprop_tensor_temp;
    OP_REQUIRES_OK(context, context->forward_input_or_allocate_temp(
                                {0}, DataTypeToEnum<double>::v(), in_shape,
                                &in_backprop_tensor_temp));
    in_backprop_tensor_temp.flat<double>().setZero();
    // Transform 4D tensor to 2D matrix.
    EigenDoubleMatrixMap in_backprop_tensor_temp_mat(
        in_backprop_tensor_temp.flat<double>().data(), in_depth,
        in_cols * in_rows * in_batch);
    ConstEigenMatrixMap out_backprop_mat(out_backprop.flat<T>().data(),
                                         out_depth,
                                         out_cols * out_rows * out_batch);
    // Loop through each element of out_backprop and evenly distribute the
    // element to the corresponding pooling cell.
    const int64 in_max_row_index = in_rows - 1;
    const int64 in_max_col_index = in_cols - 1;
    for (int64 b = 0; b < out_batch; ++b) {
      for (int64 r = 0; r < out_rows; ++r) {
        const int64 in_row_start = row_seq_tensor_flat(r);
        int64 in_row_end = overlapping_ ? row_seq_tensor_flat(r + 1)
                                        : row_seq_tensor_flat(r + 1) - 1;
        in_row_end = std::min(in_row_end, in_max_row_index);
        for (int64 c = 0; c < out_cols; ++c) {
          const int64 in_col_start = col_seq_tensor_flat(c);
          int64 in_col_end = overlapping_ ? col_seq_tensor_flat(c + 1)
                                          : col_seq_tensor_flat(c + 1) - 1;
          in_col_end = std::min(in_col_end, in_max_col_index);

          const int64 num_elements_in_pooling_cell =
              (in_row_end - in_row_start + 1) * (in_col_end - in_col_start + 1);
          const int64 out_index = (b * out_rows + r) * out_cols + c;
          // Now we can evenly distribute out_backprop(b, h, w, *) to
          // in_backprop(b, hs:he, ws:we, *).
          for (int64 in_r = in_row_start; in_r <= in_row_end; ++in_r) {
            for (int64 in_c = in_col_start; in_c <= in_col_end; ++in_c) {
              const int64 in_index = (b * in_rows + in_r) * in_cols + in_c;
              // Walk through each channel (depth).
              for (int64 d = 0; d < out_depth; ++d) {
                const double out_backprop_element = static_cast<double>(
                    out_backprop_mat.coeffRef(d, out_index));
                double& in_backprop_ref =
                    in_backprop_tensor_temp_mat.coeffRef(d, in_index);
                in_backprop_ref +=
                    out_backprop_element / num_elements_in_pooling_cell;
              }
            }
          }
        }
      }
    }

    // Depending on the type, cast double to type T.
    Tensor* in_backprop_tensor = nullptr;
    OP_REQUIRES_OK(context, context->forward_input_or_allocate_output(
                                {0}, 0, in_shape, &in_backprop_tensor));
    auto in_backprop_tensor_flat = in_backprop_tensor->flat<T>();
    auto in_backprop_tensor_temp_flat = in_backprop_tensor_temp.flat<double>();
    for (int64 i = 0; i < in_backprop_tensor_flat.size(); ++i) {
      in_backprop_tensor_flat(i) =
          static_cast<T>(in_backprop_tensor_temp_flat(i));
    }
  }

Validate inputs of `FractionalAvgPoolGrad`.

PiperOrigin-RevId: 372420640
Change-Id: Icc583928e6cdc3062e12498e4d2337a8fe3da016


int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
		     char * const exc_prop[], int exc_prop_count,
		     struct fdt_region region[], int max_regions,
		     char *path, int path_len, int add_string_tab)
{
	int stack[FDT_MAX_DEPTH] = { 0 };
	char *end;
	int nextoffset = 0;
	uint32_t tag;
	int count = 0;
	int start = -1;
	int depth = -1;
	int want = 0;
	int base = fdt_off_dt_struct(fdt);

	end = path;
	*end = '\0';
	do {
		const struct fdt_property *prop;
		const char *name;
		const char *str;
		int include = 0;
		int stop_at = 0;
		int offset;
		int len;

		offset = nextoffset;
		tag = fdt_next_tag(fdt, offset, &nextoffset);
		stop_at = nextoffset;

		switch (tag) {
		case FDT_PROP:
			include = want >= 2;
			stop_at = offset;
			prop = fdt_get_property_by_offset(fdt, offset, NULL);
			str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
			if (!str)
				return -FDT_ERR_BADSTRUCTURE;
			if (str_in_list(str, exc_prop, exc_prop_count))
				include = 0;
			break;

		case FDT_NOP:
			include = want >= 2;
			stop_at = offset;
			break;

		case FDT_BEGIN_NODE:
			depth++;
			if (depth == FDT_MAX_DEPTH)
				return -FDT_ERR_BADSTRUCTURE;
			name = fdt_get_name(fdt, offset, &len);
			if (end - path + 2 + len >= path_len)
				return -FDT_ERR_NOSPACE;
			if (end != path + 1)
				*end++ = '/';
			strcpy(end, name);
			end += len;
			stack[depth] = want;
			if (want == 1)
				stop_at = offset;
			if (str_in_list(path, inc, inc_count))
				want = 2;
			else if (want)
				want--;
			else
				stop_at = offset;
			include = want;
			break;

		case FDT_END_NODE:
			/* Depth must never go below -1 */
			if (depth < 0)
				return -FDT_ERR_BADSTRUCTURE;
			include = want;
			want = stack[depth--];
			while (end > path && *--end != '/')
				;
			*end = '\0';
			break;

		case FDT_END:
			include = 1;
			break;
		}

		if (include && start == -1) {
			/* Should we merge with previous? */
			if (count && count <= max_regions &&
			    offset == region[count - 1].offset +
					region[count - 1].size - base)
				start = region[--count].offset - base;
			else
				start = offset;
		}

		if (!include && start != -1) {
			if (count < max_regions) {
				region[count].offset = base + start;
				region[count].size = stop_at - start;
			}
			count++;
			start = -1;
		}
	} while (tag != FDT_END);

	if (nextoffset != fdt_size_dt_struct(fdt))
		return -FDT_ERR_BADLAYOUT;

	/* Add a region for the END tag and the string table */
	if (count < max_regions) {
		region[count].offset = base + start;
		region[count].size = nextoffset - start;
		if (add_string_tab)
			region[count].size += fdt_size_dt_strings(fdt);
	}
	count++;

	return count;
}

fdt_region: Check for a single root node of the correct name

At present fdt_find_regions() assumes that the FIT is a valid devicetree.
If the FIT has two root nodes this is currently not detected in this
function, nor does libfdt's fdt_check_full() notice. Also it is possible
for the root node to have a name even though it should not.

Add checks for these and return -FDT_ERR_BADSTRUCTURE if a problem is
detected.

CVE-2021-27097

Signed-off-by: Simon Glass <sjg@chromium.org>
Reported-by: Bruce Monroe <bruce.monroe@intel.com>
Reported-by: Arie Haenel <arie.haenel@intel.com>
Reported-by: Julien Lenoir <julien.lenoir@intel.com>


static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field)
{
    pb_field_iter_t old_field = *field;
    pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */
    pb_size_t new_tag = field->tag; /* New which_ value */

    if (old_tag == 0)
        return true; /* Ok, no old data in union */

    if (old_tag == new_tag)
        return true; /* Ok, old data is of same type => merge */

    /* Release old data. The find can fail if the message struct contains
     * invalid data. */
    if (!pb_field_iter_find(&old_field, old_tag))
        PB_RETURN_ERROR(stream, "invalid union tag");

    pb_release_single_field(&old_field);

    return true;
}

Fix invalid free() with oneof (#647)

Nanopb would call free() or realloc() on an invalid
(attacker controlled) pointer value when all the following
conditions are true:

- PB_ENABLE_MALLOC is defined at the compile time
- Message definition contains an oneof field, and the oneof
  contains at least one pointer type field and at least one
  non-pointer type field.
- Data being decoded first contains a non-pointer value for
  the oneof field, and later contains an overwriting pointer
  value.

Depending on message layout, the bug may not be exploitable in all
cases, but it is known to be exploitable at least with string and
bytes fields. Actual security impact will also depend on the heap
implementation used.

static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);

static bool checkreturn

static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);

static bool checkreturn check_wire_type(pb_wire_type_t wire_type, pb_field_iter_t *field);

static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);

static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);

static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);

static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);

static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);

static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);

static bool pb_message_set_to_defaults(pb_field_iter_t *iter);

static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_fixed(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field);

static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size);

static void initialize_pointer_field(void *pItem, pb_field_iter_t *field);

static void pb_release_single_field(pb_field_iter_t *field);

typedef struct {
    uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32];
} pb_fields_seen_t;


void ndpi_search_h323(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
{
  struct ndpi_packet_struct *packet = &flow->packet;
  u_int16_t dport = 0, sport = 0;

  NDPI_LOG_DBG(ndpi_struct, "search H323\n");

  /*
    The TPKT protocol is used by ISO 8072 (on port 102)
    and H.323. So this check below is to avoid ambiguities
  */
  if((packet->tcp != NULL) && (packet->tcp->dest != ntohs(102))) {
    NDPI_LOG_DBG2(ndpi_struct, "calculated dport over tcp\n");

    /* H323  */
    if(packet->payload_packet_len >= 4
       && (packet->payload[0] == 0x03)
       && (packet->payload[1] == 0x00)) {
	struct tpkt *t = (struct tpkt*)packet->payload;
	u_int16_t len = ntohs(t->len);

	if(packet->payload_packet_len == len) {
	  /*
	    We need to check if this packet is in reality
	    a RDP (Remote Desktop) packet encapsulated on TPTK
	   */

	  if(packet->payload[4] == (packet->payload_packet_len - sizeof(struct tpkt) - 1)) {
	    /* ISO 8073/X.224 */
	    if((packet->payload[5] == 0xE0 /* CC Connect Request */)
	       || (packet->payload[5] == 0xD0 /* CC Connect Confirm */)) {
	      NDPI_LOG_INFO(ndpi_struct, "found RDP\n");
	      ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_RDP, NDPI_PROTOCOL_UNKNOWN);
	      return;
	    }
	  }

	  flow->l4.tcp.h323_valid_packets++;

	  if(flow->l4.tcp.h323_valid_packets >= 2) {
	    NDPI_LOG_INFO(ndpi_struct, "found H323 broadcast\n");
	    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_H323, NDPI_PROTOCOL_UNKNOWN);
	  }
	} else {
	  /* This is not H.323 */
	  NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
	  return;
	}
      }
  } else if(packet->udp != NULL) {
    sport = ntohs(packet->udp->source), dport = ntohs(packet->udp->dest);
    NDPI_LOG_DBG2(ndpi_struct, "calculated dport over udp\n");

    if(packet->payload_packet_len >= 6 && packet->payload[0] == 0x80 && packet->payload[1] == 0x08 &&
       (packet->payload[2] == 0xe7 || packet->payload[2] == 0x26) &&
       packet->payload[4] == 0x00 && packet->payload[5] == 0x00)
      {
	NDPI_LOG_INFO(ndpi_struct, "found H323 broadcast\n");
	ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_H323, NDPI_PROTOCOL_UNKNOWN);
	return;
      }
    /* H323  */
    if(sport == 1719 || dport == 1719)
      {
        if(packet->payload[0] == 0x16 && packet->payload[1] == 0x80 && packet->payload[4] == 0x06 && packet->payload[5] == 0x00)
	  {
	    NDPI_LOG_INFO(ndpi_struct, "found H323 broadcast\n");
	    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_H323, NDPI_PROTOCOL_UNKNOWN);
	    return;
	  }
        else if(packet->payload_packet_len >= 20 && packet->payload_packet_len <= 117)
	  {
	    NDPI_LOG_INFO(ndpi_struct, "found H323 broadcast\n");
	    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_H323, NDPI_PROTOCOL_UNKNOWN);
	    return;
	  }
        else
	  {
	    NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
	    return;
	  }
      }
  }

}

Added fix to avoid potential heap buffer overflow in H.323 dissector
Modified HTTP report information to make it closer to the HTTP field names

#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_H323

struct tpkt {
  u_int8_t version, reserved;
  u_int16_t len;
}


Status ConvBackpropComputeDimensionsV2(
    StringPiece label, int num_spatial_dims, const TensorShape& input_shape,
    const TensorShape& filter_shape, const TensorShape& out_backprop_shape,
    const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
    Padding padding, absl::Span<const int64> explicit_paddings,
    TensorFormat data_format, ConvBackpropDimensions* dims) {
  // The + 2 in the following line is for the batch and feature dimensions.
  const int num_dims = num_spatial_dims + 2;
  if (input_shape.dims() != num_dims) {
    return errors::InvalidArgument(label, ": input must be ", num_dims,
                                   "-dimensional");
  }
  if (filter_shape.dims() != num_dims) {
    return errors::InvalidArgument(label, ": filter must be ", num_dims,
                                   "-dimensional");
  }
  if (out_backprop_shape.dims() != num_dims) {
    return errors::InvalidArgument(label, ": out_backprop must be ", num_dims,
                                   "-dimensional");
  }
  int batch_dim = GetTensorBatchDimIndex(num_dims, data_format);
  dims->batch_size = input_shape.dim_size(batch_dim);
  if (dims->batch_size != out_backprop_shape.dim_size(batch_dim)) {
    return errors::InvalidArgument(
        label, ": input and out_backprop must have the same batch size.",
        " Input batch: ", dims->batch_size,
        ", outbackprop batch: ", out_backprop_shape.dim_size(batch_dim),
        ", batch_dim: ", batch_dim);
  }

  int feature_dim = GetTensorFeatureDimIndex(num_dims, data_format);
  dims->in_depth = input_shape.dim_size(feature_dim);
  // The input and output feature dimensions are the second last and last
  // dimensions of the filter Tensor.
  VLOG(2) << "input vs filter_in depth " << dims->in_depth << " "
          << filter_shape.dim_size(num_dims - 2);
  if (dims->in_depth % filter_shape.dim_size(num_dims - 2)) {
    return errors::InvalidArgument(
        label, ": input depth must be evenly divisible by filter depth");
  }
  dims->out_depth = filter_shape.dim_size(num_dims - 1);
  if (dims->out_depth != out_backprop_shape.dim_size(feature_dim)) {
    return errors::InvalidArgument(
        label, ": filter and out_backprop must have the same out_depth");
  }
  dims->spatial_dims.resize(num_spatial_dims);
  for (int i = 0; i < num_spatial_dims; ++i) {
    int image_dim = GetTensorSpatialDimIndex(num_dims, data_format, i);
    int64 padding_before = -1, padding_after = -1;
    if (padding == EXPLICIT) {
      padding_before = explicit_paddings[2 * image_dim];
      padding_after = explicit_paddings[2 * image_dim + 1];
    }
    TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimension(
        label, input_shape, filter_shape, out_backprop_shape, dilations,
        strides, padding, padding_before, padding_after, image_dim, i,
        &dims->spatial_dims[i]));
  }
  return Status::OK();
}

Prevent another division by zero.

PiperOrigin-RevId: 369338598
Change-Id: I55471d363e401fdcf8d259670ad4eef672b731e2

#define EIGEN_USE_THREADS

#define USE_EIGEN_TENSOR


  void Compute(OpKernelContext *ctx) override {
    // (0) validations
    const Tensor *a_indices, *b_indices, *a_values_t, *b_values_t, *a_shape,
        *b_shape, *thresh_t;

    OP_REQUIRES_OK(ctx, ctx->input("a_indices", &a_indices));
    OP_REQUIRES_OK(ctx, ctx->input("b_indices", &b_indices));
    OP_REQUIRES(ctx,
                TensorShapeUtils::IsMatrix(a_indices->shape()) &&
                    TensorShapeUtils::IsMatrix(b_indices->shape()),
                errors::InvalidArgument(
                    "Input indices should be matrices but received shapes: ",
                    a_indices->shape().DebugString(), " and ",
                    b_indices->shape().DebugString()));
    const int64 a_nnz = a_indices->dim_size(0);
    const int64 b_nnz = b_indices->dim_size(0);

    OP_REQUIRES_OK(ctx, ctx->input("a_values", &a_values_t));
    OP_REQUIRES_OK(ctx, ctx->input("b_values", &b_values_t));

    OP_REQUIRES(ctx,
                TensorShapeUtils::IsVector(a_values_t->shape()) &&
                    TensorShapeUtils::IsVector(b_values_t->shape()),
                errors::InvalidArgument(
                    "Input values should be vectors but received shapes: ",
                    a_values_t->shape().DebugString(), " and ",
                    b_values_t->shape().DebugString()));
    auto a_values = ctx->input(1).vec<T>();
    auto b_values = ctx->input(4).vec<T>();
    OP_REQUIRES(
        ctx, a_values.size() == a_nnz && b_values.size() == b_nnz,
        errors::InvalidArgument("Expected ", a_nnz, " and ", b_nnz,
                                " non-empty input values, got ",
                                a_values.size(), " and ", b_values.size()));

    OP_REQUIRES_OK(ctx, ctx->input("a_shape", &a_shape));
    OP_REQUIRES_OK(ctx, ctx->input("b_shape", &b_shape));
    OP_REQUIRES(ctx,
                TensorShapeUtils::IsVector(a_shape->shape()) &&
                    TensorShapeUtils::IsVector(b_shape->shape()),
                errors::InvalidArgument(
                    "Input shapes should be a vector but received shapes ",
                    a_shape->shape().DebugString(), " and ",
                    b_shape->shape().DebugString()));
    OP_REQUIRES(
        ctx, a_shape->IsSameSize(*b_shape),
        errors::InvalidArgument(
            "Operands do not have the same ranks; got shapes: ",
            a_shape->SummarizeValue(10), " and ", b_shape->SummarizeValue(10)));
    const auto a_shape_flat = a_shape->flat<int64>();
    const auto b_shape_flat = b_shape->flat<int64>();
    for (int i = 0; i < a_shape->NumElements(); ++i) {
      OP_REQUIRES(ctx, a_shape_flat(i) == b_shape_flat(i),
                  errors::InvalidArgument(
                      "Operands' shapes do not match: got ", a_shape_flat(i),
                      " and ", b_shape_flat(i), " for dimension ", i));
    }

    OP_REQUIRES_OK(ctx, ctx->input("thresh", &thresh_t));
    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(thresh_t->shape()),
                errors::InvalidArgument(
                    "The magnitude threshold must be a scalar: got shape ",
                    thresh_t->shape().DebugString()));
    // std::abs() so that it works for complex{64,128} values as well
    const Treal thresh = thresh_t->scalar<Treal>()();

    // (1) do a pass over inputs, and append values and indices to vectors
    auto a_indices_mat = a_indices->matrix<int64>();
    auto b_indices_mat = b_indices->matrix<int64>();
    std::vector<std::pair<bool, int64>> entries_to_copy;  // from_a?, idx
    entries_to_copy.reserve(a_nnz + b_nnz);
    std::vector<T> out_values;
    const int num_dims = a_shape->dim_size(0);

    // The input and output sparse tensors are assumed to be ordered along
    // increasing dimension number.
    int64 i = 0, j = 0;
    T s;
    while (i < a_nnz && j < b_nnz) {
      switch (sparse::DimComparator::cmp(a_indices_mat, b_indices_mat, i, j,
                                         num_dims)) {
        case -1:
          entries_to_copy.emplace_back(true, i);
          out_values.push_back(a_values(i));
          ++i;
          break;
        case 0:
          s = a_values(i) + b_values(j);
          if (thresh <= std::abs(s)) {
            entries_to_copy.emplace_back(true, i);
            out_values.push_back(s);
          }
          ++i;
          ++j;
          break;
        case 1:
          entries_to_copy.emplace_back(false, j);
          out_values.push_back(b_values(j));
          ++j;
          break;
      }
    }

#define HANDLE_LEFTOVERS(A_OR_B, IDX, IS_A)     \
  while (IDX < A_OR_B##_nnz) {                  \
    entries_to_copy.emplace_back(IS_A, IDX);    \
    out_values.push_back(A_OR_B##_values(IDX)); \
    ++IDX;                                      \
  }

    // at most one of these calls appends new values
    HANDLE_LEFTOVERS(a, i, true);
    HANDLE_LEFTOVERS(b, j, false);
#undef HANDLE_LEFTOVERS

    // (2) allocate and fill output tensors
    const int64 sum_nnz = out_values.size();
    Tensor *out_indices_t, *out_values_t;
    OP_REQUIRES_OK(ctx,
                   ctx->allocate_output(0, TensorShape({sum_nnz, num_dims}),
                                        &out_indices_t));
    OP_REQUIRES_OK(
        ctx, ctx->allocate_output(1, TensorShape({sum_nnz}), &out_values_t));
    auto out_indices_mat = out_indices_t->matrix<int64>();
    auto out_values_flat = out_values_t->vec<T>();

    for (i = 0; i < sum_nnz; ++i) {
      const bool from_a = entries_to_copy[i].first;
      const int64 idx = entries_to_copy[i].second;
      out_indices_mat.chip<0>(i) =
          from_a ? a_indices_mat.chip<0>(idx) : b_indices_mat.chip<0>(idx);
    }
    if (sum_nnz > 0) {
      std::copy_n(out_values.begin(), sum_nnz, &out_values_flat(0));
    }
    ctx->set_output(2, *a_shape);
  }

Fix `tf.raw_ops.SparseAdd ` invalid memory access failure.

PiperOrigin-RevId: 370568774
Change-Id: I5f73b31c865f2948a1c8dfb7ebd22b3cfb6405bf

/* No explicit global definitions found. */


std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMode, GetLengthTarget target)
{
	std::vector<GetLengthType> results;
	GetLengthType retval;
	retval.startOrder = target.startOrder;
	retval.startRow = target.startRow;

	// Are we trying to reach a certain pattern position?
	const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget;
	const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions;

	SEQUENCEINDEX sequence = target.sequence;
	if(sequence >= Order.GetNumSequences()) sequence = Order.GetCurrentSequenceIndex();
	const ModSequence &orderList = Order(sequence);

	GetLengthMemory memory(*this);
	CSoundFile::PlayState &playState = *memory.state;
	// Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time)
	RowVisitor visitedRows(*this, sequence);

	playState.m_nNextRow = playState.m_nRow = target.startRow;
	playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;

	// Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode.
	std::bitset<MAX_EFFECTS> forbiddenCommands;
	std::bitset<MAX_VOLCMDS> forbiddenVolCommands;

	if(adjustSamplePos)
	{
		forbiddenCommands.set(CMD_ARPEGGIO);             forbiddenCommands.set(CMD_PORTAMENTOUP);
		forbiddenCommands.set(CMD_PORTAMENTODOWN);       forbiddenCommands.set(CMD_XFINEPORTAUPDOWN);
		forbiddenCommands.set(CMD_NOTESLIDEUP);          forbiddenCommands.set(CMD_NOTESLIDEUPRETRIG);
		forbiddenCommands.set(CMD_NOTESLIDEDOWN);        forbiddenCommands.set(CMD_NOTESLIDEDOWNRETRIG);
		forbiddenVolCommands.set(VOLCMD_PORTAUP);        forbiddenVolCommands.set(VOLCMD_PORTADOWN);

		// Optimize away channels for which it's pointless to adjust sample positions
		for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
		{
			if(ChnSettings[i].dwFlags[CHN_MUTE]) memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL;
		}
		if(target.mode == GetLengthTarget::SeekPosition && target.pos.order < orderList.size())
		{
			// If we know where to seek, we can directly rule out any channels on which a new note would be triggered right at the start.
			const PATTERNINDEX seekPat = orderList[target.pos.order];
			if(Patterns.IsValidPat(seekPat) && Patterns[seekPat].IsValidRow(target.pos.row))
			{
				const ModCommand *m = Patterns[seekPat].GetRow(target.pos.row);
				for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++)
				{
					if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments())
						|| (m->IsNote() && !m->IsPortamento()))
					{
						memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL;
					}
				}
			}
		}
	}

	// If samples are being synced, force them to resync if tick duration changes
	uint32 oldTickDuration = 0;

	for (;;)
	{
		// Time target reached.
		if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time)
		{
			retval.targetReached = true;
			break;
		}

		uint32 rowDelay = 0, tickDelay = 0;
		playState.m_nRow = playState.m_nNextRow;
		playState.m_nCurrentOrder = playState.m_nNextOrder;

		if(orderList.IsValidPat(playState.m_nCurrentOrder) && playState.m_nRow >= Patterns[orderList[playState.m_nCurrentOrder]].GetNumRows())
		{
			playState.m_nRow = 0;
			if(m_playBehaviour[kFT2LoopE60Restart])
			{
				playState.m_nRow = playState.m_nNextPatStartRow;
				playState.m_nNextPatStartRow = 0;
			}
			playState.m_nCurrentOrder = ++playState.m_nNextOrder;
		}

		// Check if pattern is valid
		playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
		bool positionJumpOnThisRow = false;
		bool patternBreakOnThisRow = false;
		bool patternLoopEndedOnThisRow = false, patternLoopStartedOnThisRow = false;

		if(!Patterns.IsValidPat(playState.m_nPattern) && playState.m_nPattern != orderList.GetInvalidPatIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
		{
			// Early test: Target is inside +++ or non-existing pattern
			retval.targetReached = true;
			break;
		}

		while(playState.m_nPattern >= Patterns.Size())
		{
			// End of song?
			if((playState.m_nPattern == orderList.GetInvalidPatIndex()) || (playState.m_nCurrentOrder >= orderList.size()))
			{
				if(playState.m_nCurrentOrder == orderList.GetRestartPos())
					break;
				else
					playState.m_nCurrentOrder = orderList.GetRestartPos();
			} else
			{
				playState.m_nCurrentOrder++;
			}
			playState.m_nPattern = (playState.m_nCurrentOrder < orderList.size()) ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
			playState.m_nNextOrder = playState.m_nCurrentOrder;
			if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.IsVisited(playState.m_nCurrentOrder, 0, true))
			{
				if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
				{
					// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
					break;
				} else
				{
					// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
					retval.duration = memory.elapsedTime;
					results.push_back(retval);
					retval.startRow = playState.m_nRow;
					retval.startOrder = playState.m_nNextOrder;
					memory.Reset();

					playState.m_nCurrentOrder = playState.m_nNextOrder;
					playState.m_nPattern = orderList[playState.m_nCurrentOrder];
					playState.m_nNextRow = playState.m_nRow;
					break;
				}
			}
		}
		if(playState.m_nNextOrder == ORDERINDEX_INVALID)
		{
			// GetFirstUnvisitedRow failed, so there is nothing more to play
			break;
		}

		// Skip non-existing patterns
		if(!Patterns.IsValidPat(playState.m_nPattern))
		{
			// If there isn't even a tune, we should probably stop here.
			if(playState.m_nCurrentOrder == orderList.GetRestartPos())
			{
				if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
				{
					// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
					break;
				} else
				{
					// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
					retval.duration = memory.elapsedTime;
					results.push_back(retval);
					retval.startRow = playState.m_nRow;
					retval.startOrder = playState.m_nNextOrder;
					memory.Reset();
					playState.m_nNextRow = playState.m_nRow;
					continue;
				}
			}
			playState.m_nNextOrder = playState.m_nCurrentOrder + 1;
			continue;
		}
		// Should never happen
		if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows())
			playState.m_nRow = 0;

		// Check whether target was reached.
		if(target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order && playState.m_nRow == target.pos.row)
		{
			retval.targetReached = true;
			break;
		}

		if(visitedRows.IsVisited(playState.m_nCurrentOrder, playState.m_nRow, true))
		{
			if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
			{
				// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
				break;
			} else
			{
				// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
				retval.duration = memory.elapsedTime;
				results.push_back(retval);
				retval.startRow = playState.m_nRow;
				retval.startOrder = playState.m_nNextOrder;
				memory.Reset();
				playState.m_nNextRow = playState.m_nRow;
				continue;
			}
		}

		retval.endOrder = playState.m_nCurrentOrder;
		retval.endRow = playState.m_nRow;

		// Update next position
		playState.m_nNextRow = playState.m_nRow + 1;

		// Jumped to invalid pattern row?
		if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows())
		{
			playState.m_nRow = 0;
		}
		// New pattern?
		if(!playState.m_nRow)
		{
			for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
			{
				memory.chnSettings[chn].patLoop = memory.elapsedTime;
				memory.chnSettings[chn].patLoopSmp = playState.m_lTotalSampleCount;
			}
		}

		ModChannel *pChn = playState.Chn;

		// For various effects, we need to know first how many ticks there are in this row.
		const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0);
		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++)
		{
			if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE])	// not even effects are processed on muted S3M channels
				continue;
			if(p->IsPcNote())
			{
#ifndef NO_PLUGINS
				if((adjustMode & eAdjust) && p->instr > 0 && p->instr <= MAX_MIXPLUGINS)
				{
					memory.plugParams[std::make_pair(p->instr, p->GetValueVolCol())] = p->GetValueEffectCol();
				}
#endif // NO_PLUGINS
				pChn[nChn].rowCommand.Clear();
				continue;
			}
			pChn[nChn].rowCommand = *p;
			switch(p->command)
			{
			case CMD_SPEED:
				SetSpeed(playState, p->param);
				break;

			case CMD_TEMPO:
				if(m_playBehaviour[kMODVBlankTiming])
				{
					// ProTracker MODs with VBlank timing: All Fxx parameters set the tick count.
					if(p->param != 0) SetSpeed(playState, p->param);
				}
				break;

			case CMD_S3MCMDEX:
				if((p->param & 0xF0) == 0x60)
				{
					// Fine Pattern Delay
					tickDelay += (p->param & 0x0F);
				} else if((p->param & 0xF0) == 0xE0 && !rowDelay)
				{
					// Pattern Delay
					if(!(GetType() & MOD_TYPE_S3M) || (p->param & 0x0F) != 0)
					{
						// While Impulse Tracker *does* count S60 as a valid row delay (and thus ignores any other row delay commands on the right),
						// Scream Tracker 3 simply ignores such commands.
						rowDelay = 1 + (p->param & 0x0F);
					}
				}
				break;

			case CMD_MODCMDEX:
				if((p->param & 0xF0) == 0xE0)
				{
					// Pattern Delay
					rowDelay = 1 + (p->param & 0x0F);
				}
				break;
			}
		}
		if(rowDelay == 0) rowDelay = 1;
		const uint32 numTicks = (playState.m_nMusicSpeed + tickDelay) * rowDelay;
		const uint32 nonRowTicks = numTicks - rowDelay;

		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); pChn++, nChn++) if(!pChn->rowCommand.IsEmpty())
		{
			if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE])	// not even effects are processed on muted S3M channels
				continue;
			ModCommand::COMMAND command = pChn->rowCommand.command;
			ModCommand::PARAM param = pChn->rowCommand.param;
			ModCommand::NOTE note = pChn->rowCommand.note;

			if (pChn->rowCommand.instr)
			{
				pChn->nNewIns = pChn->rowCommand.instr;
				pChn->nLastNote = NOTE_NONE;
				memory.chnSettings[nChn].vol = 0xFF;
			}
			if (pChn->rowCommand.IsNote()) pChn->nLastNote = note;

			// Update channel panning
			if(pChn->rowCommand.IsNote() || pChn->rowCommand.instr)
			{
				SAMPLEINDEX smp = 0;
				if(GetNumInstruments())
				{
					ModInstrument *pIns;
					if(pChn->nNewIns <= GetNumInstruments() && (pIns = Instruments[pChn->nNewIns]) != nullptr)
					{
						if(pIns->dwFlags[INS_SETPANNING])
							pChn->nPan = pIns->nPan;
						if(ModCommand::IsNote(note))
							smp = pIns->Keyboard[note - NOTE_MIN];
					}
				} else
				{
					smp = pChn->nNewIns;
				}
				if(smp > 0 && smp <= GetNumSamples() && Samples[smp].uFlags[CHN_PANNING])
				{
					pChn->nPan = Samples[smp].nPan;
				}
			}

			switch(pChn->rowCommand.volcmd)
			{
			case VOLCMD_VOLUME:
				memory.chnSettings[nChn].vol = pChn->rowCommand.vol;
				break;
			case VOLCMD_VOLSLIDEUP:
			case VOLCMD_VOLSLIDEDOWN:
				if(pChn->rowCommand.vol != 0)
					pChn->nOldVolParam = pChn->rowCommand.vol;
				break;
			}

			switch(command)
			{
			// Position Jump
			case CMD_POSITIONJUMP:
				positionJumpOnThisRow = true;
				playState.m_nNextOrder = static_cast<ORDERINDEX>(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn));
				playState.m_nNextPatStartRow = 0;  // FT2 E60 bug
				// see https://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx
				// Test case: PatternJump.mod
				if(!patternBreakOnThisRow || (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)))
					playState.m_nNextRow = 0;

				if (adjustMode & eAdjust)
				{
					pChn->nPatternLoopCount = 0;
					pChn->nPatternLoop = 0;
				}
				break;
			// Pattern Break
			case CMD_PATTERNBREAK:
				{
					ROWINDEX row = PatternBreak(playState, nChn, param);
					if(row != ROWINDEX_INVALID)
					{
						patternBreakOnThisRow = true;
						playState.m_nNextRow = row;

						if(!positionJumpOnThisRow)
						{
							playState.m_nNextOrder = playState.m_nCurrentOrder + 1;
						}
						if(adjustMode & eAdjust)
						{
							pChn->nPatternLoopCount = 0;
							pChn->nPatternLoop = 0;
						}
					}
				}
				break;
			// Set Tempo
			case CMD_TEMPO:
				if(!m_playBehaviour[kMODVBlankTiming])
				{
					TEMPO tempo(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn), 0);
					if ((adjustMode & eAdjust) && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
					{
						if (tempo.GetInt()) pChn->nOldTempo = static_cast<uint8>(tempo.GetInt()); else tempo.Set(pChn->nOldTempo);
					}

					if (tempo.GetInt() >= 0x20) playState.m_nMusicTempo = tempo;
					else
					{
						// Tempo Slide
						TEMPO tempoDiff((tempo.GetInt() & 0x0F) * nonRowTicks, 0);
						if ((tempo.GetInt() & 0xF0) == 0x10)
						{
							playState.m_nMusicTempo += tempoDiff;
						} else
						{
							if(tempoDiff < playState.m_nMusicTempo)
								playState.m_nMusicTempo -= tempoDiff;
							else
								playState.m_nMusicTempo.Set(0);
						}
					}

					TEMPO tempoMin = GetModSpecifications().GetTempoMin(), tempoMax = GetModSpecifications().GetTempoMax();
					if(m_playBehaviour[kTempoClamp])	// clamp tempo correctly in compatible mode
					{
						tempoMax.Set(255);
					}
					Limit(playState.m_nMusicTempo, tempoMin, tempoMax);
				}
				break;

			case CMD_S3MCMDEX:
				switch(param & 0xF0)
				{
				case 0x90:
					if(param <= 0x91)
					{
						pChn->dwFlags.set(CHN_SURROUND, param == 0x91);
					}
					break;

				case 0xA0:
					// High sample offset
					pChn->nOldHiOffset = param & 0x0F;
					break;

				case 0xB0:
					// Pattern Loop
					if (param & 0x0F)
					{
						patternLoopEndedOnThisRow = true;
					} else
					{
						CHANNELINDEX firstChn = nChn, lastChn = nChn;
						if(GetType() == MOD_TYPE_S3M)
						{
							// ST3 has only one global loop memory.
							firstChn = 0;
							lastChn = GetNumChannels() - 1;
						}
						for(CHANNELINDEX c = firstChn; c <= lastChn; c++)
						{
							memory.chnSettings[c].patLoop = memory.elapsedTime;
							memory.chnSettings[c].patLoopSmp = playState.m_lTotalSampleCount;
							memory.chnSettings[c].patLoopStart = playState.m_nRow;
						}
						patternLoopStartedOnThisRow = true;
					}
					break;

				case 0xF0:
					// Active macro
					pChn->nActiveMacro = param & 0x0F;
					break;
				}
				break;

			case CMD_MODCMDEX:
				switch(param & 0xF0)
				{
				case 0x60:
					// Pattern Loop
					if (param & 0x0F)
					{
						playState.m_nNextPatStartRow = memory.chnSettings[nChn].patLoopStart; // FT2 E60 bug
						patternLoopEndedOnThisRow = true;
					} else
					{
						patternLoopStartedOnThisRow = true;
						memory.chnSettings[nChn].patLoop = memory.elapsedTime;
						memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
						memory.chnSettings[nChn].patLoopStart = playState.m_nRow;
					}
					break;

				case 0xF0:
					// Active macro
					pChn->nActiveMacro = param & 0x0F;
					break;
				}
				break;

			case CMD_XFINEPORTAUPDOWN:
				// ignore high offset in compatible mode
				if(((param & 0xF0) == 0xA0) && !m_playBehaviour[kFT2RestrictXCommand]) pChn->nOldHiOffset = param & 0x0F;
				break;
			}

			// The following calculations are not interesting if we just want to get the song length.
			if (!(adjustMode & eAdjust)) continue;
			switch(command)
			{
			// Portamento Up/Down
			case CMD_PORTAMENTOUP:
				if(param)
				{
					// FT2 compatibility: Separate effect memory for all portamento commands
					// Test case: Porta-LinkMem.xm
					if(!m_playBehaviour[kFT2PortaUpDownMemory])
						pChn->nOldPortaDown = param;
					pChn->nOldPortaUp = param;
				}
				break;
			case CMD_PORTAMENTODOWN:
				if(param)
				{
					// FT2 compatibility: Separate effect memory for all portamento commands
					// Test case: Porta-LinkMem.xm
					if(!m_playBehaviour[kFT2PortaUpDownMemory])
						pChn->nOldPortaUp = param;
					pChn->nOldPortaDown = param;
				}
				break;
			// Tone-Portamento
			case CMD_TONEPORTAMENTO:
				if (param) pChn->nPortamentoSlide = param << 2;
				break;
			// Offset
			case CMD_OFFSET:
				if (param) pChn->oldOffset = param << 8;
				break;
			// Volume Slide
			case CMD_VOLUMESLIDE:
			case CMD_TONEPORTAVOL:
				if (param) pChn->nOldVolumeSlide = param;
				break;
			// Set Volume
			case CMD_VOLUME:
				memory.chnSettings[nChn].vol = param;
				break;
			// Global Volume
			case CMD_GLOBALVOLUME:
				if(!(GetType() & GLOBALVOL_7BIT_FORMATS) && param < 128) param *= 2;
				// IT compatibility 16. ST3 and IT ignore out-of-range values
				if(param <= 128)
				{
					playState.m_nGlobalVolume = param * 2;
				} else if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)))
				{
					playState.m_nGlobalVolume = 256;
				}
				break;
			// Global Volume Slide
			case CMD_GLOBALVOLSLIDE:
				if(m_playBehaviour[kPerChannelGlobalVolSlide])
				{
					// IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
					if (param) pChn->nOldGlobalVolSlide = param; else param = pChn->nOldGlobalVolSlide;
				} else
				{
					if (param) playState.Chn[0].nOldGlobalVolSlide = param; else param = playState.Chn[0].nOldGlobalVolSlide;
				}
				if (((param & 0x0F) == 0x0F) && (param & 0xF0))
				{
					param >>= 4;
					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
					playState.m_nGlobalVolume += param << 1;
				} else if (((param & 0xF0) == 0xF0) && (param & 0x0F))
				{
					param = (param & 0x0F) << 1;
					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
					playState.m_nGlobalVolume -= param;
				} else if (param & 0xF0)
				{
					param >>= 4;
					param <<= 1;
					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
					playState.m_nGlobalVolume += param * nonRowTicks;
				} else
				{
					param = (param & 0x0F) << 1;
					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
					playState.m_nGlobalVolume -= param * nonRowTicks;
				}
				Limit(playState.m_nGlobalVolume, 0, 256);
				break;
			case CMD_CHANNELVOLUME:
				if (param <= 64) pChn->nGlobalVol = param;
				break;
			case CMD_CHANNELVOLSLIDE:
				{
					if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide;
					int32 volume = pChn->nGlobalVol;
					if((param & 0x0F) == 0x0F && (param & 0xF0))
						volume += (param >> 4);		// Fine Up
					else if((param & 0xF0) == 0xF0 && (param & 0x0F))
						volume -= (param & 0x0F);	// Fine Down
					else if(param & 0x0F)			// Down
						volume -= (param & 0x0F) * nonRowTicks;
					else							// Up
						volume += ((param & 0xF0) >> 4) * nonRowTicks;
					Limit(volume, 0, 64);
					pChn->nGlobalVol = volume;
				}
				break;
			case CMD_PANNING8:
				Panning(pChn, param, Pan8bit);
				break;
			case CMD_MODCMDEX:
				if(param < 0x10)
				{
					// LED filter
					for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
					{
						playState.Chn[chn].dwFlags.set(CHN_AMIGAFILTER, !(param & 1));
					}
				}
				MPT_FALLTHROUGH;
			case CMD_S3MCMDEX:
				if((param & 0xF0) == 0x80)
				{
					Panning(pChn, (param & 0x0F), Pan4bit);
				}
				break;

			case CMD_VIBRATOVOL:
				if (param) pChn->nOldVolumeSlide = param;
				param = 0;
				MPT_FALLTHROUGH;
			case CMD_VIBRATO:
				Vibrato(pChn, param);
				break;
			case CMD_FINEVIBRATO:
				FineVibrato(pChn, param);
				break;
			case CMD_TREMOLO:
				Tremolo(pChn, param);
				break;
			case CMD_PANBRELLO:
				Panbrello(pChn, param);
				break;
			}

			switch(pChn->rowCommand.volcmd)
			{
			case VOLCMD_PANNING:
				Panning(pChn, pChn->rowCommand.vol, Pan6bit);
				break;

			case VOLCMD_VIBRATOSPEED:
				// FT2 does not automatically enable vibrato with the "set vibrato speed" command
				if(m_playBehaviour[kFT2VolColVibrato])
					pChn->nVibratoSpeed = pChn->rowCommand.vol & 0x0F;
				else
					Vibrato(pChn, pChn->rowCommand.vol << 4);
				break;
			case VOLCMD_VIBRATODEPTH:
				Vibrato(pChn, pChn->rowCommand.vol);
				break;
			}

			// Process vibrato / tremolo / panbrello
			switch(pChn->rowCommand.command)
			{
			case CMD_VIBRATO:
			case CMD_FINEVIBRATO:
			case CMD_VIBRATOVOL:
				if(adjustMode & eAdjust)
				{
					uint32 vibTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks;
					uint32 inc = pChn->nVibratoSpeed * vibTicks;
					if(m_playBehaviour[kITVibratoTremoloPanbrello])
						inc *= 4;
					pChn->nVibratoPos += static_cast<uint8>(inc);
				}
				break;

			case CMD_TREMOLO:
				if(adjustMode & eAdjust)
				{
					uint32 tremTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks;
					uint32 inc = pChn->nTremoloSpeed * tremTicks;
					if(m_playBehaviour[kITVibratoTremoloPanbrello])
						inc *= 4;
					pChn->nTremoloPos += static_cast<uint8>(inc);
				}
				break;

			case CMD_PANBRELLO:
				if(adjustMode & eAdjust)
				{
					// Panbrello effect is permanent in compatible mode, so actually apply panbrello for the last tick of this row
					pChn->nPanbrelloPos += static_cast<uint8>(pChn->nPanbrelloSpeed * (numTicks - 1));
					ProcessPanbrello(pChn);
				}
				break;
			}
		}

		// Interpret F00 effect in XM files as "stop song"
		if(GetType() == MOD_TYPE_XM && playState.m_nMusicSpeed == uint16_max)
		{
			break;
		}

		playState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
		if(Patterns[playState.m_nPattern].GetOverrideSignature())
		{
			playState.m_nCurrentRowsPerBeat = Patterns[playState.m_nPattern].GetRowsPerBeat();
		}

		const uint32 tickDuration = GetTickDuration(playState);
		const uint32 rowDuration = tickDuration * numTicks;
		memory.elapsedTime += static_cast<double>(rowDuration) / static_cast<double>(m_MixerSettings.gdwMixingFreq);
		playState.m_lTotalSampleCount += rowDuration;

		if(adjustSamplePos)
		{
			// Super experimental and dirty sample seeking
			pChn = playState.Chn;
			for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); pChn++, nChn++)
			{
				if(memory.chnSettings[nChn].ticksToRender == GetLengthMemory::IGNORE_CHANNEL)
					continue;

				uint32 startTick = 0;
				const ModCommand &m = pChn->rowCommand;
				uint32 paramHi = m.param >> 4, paramLo = m.param & 0x0F;
				bool porta = m.command == CMD_TONEPORTAMENTO || m.command == CMD_TONEPORTAVOL || m.volcmd == VOLCMD_TONEPORTAMENTO;
				bool stopNote = patternLoopStartedOnThisRow;	// It's too much trouble to keep those pattern loops in sync...

				if(m.instr) pChn->proTrackerOffset = 0;
				if(m.IsNote())
				{
					if(porta && memory.chnSettings[nChn].incChanged)
					{
						// If there's a portamento, the current channel increment mustn't be 0 in NoteChange()
						pChn->increment = GetChannelIncrement(pChn, pChn->nPeriod, 0);
					}
					int32 setPan = pChn->nPan;
					pChn->nNewNote = pChn->nLastNote;
					if(pChn->nNewIns != 0) InstrumentChange(pChn, pChn->nNewIns, porta);
					NoteChange(pChn, m.note, porta);
					memory.chnSettings[nChn].incChanged = true;

					if((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xD0 && paramLo < numTicks)
					{
						startTick = paramLo;
					} else if(m.command == CMD_DELAYCUT && paramHi < numTicks)
					{
						startTick = paramHi;
					}
					if(rowDelay > 1 && startTick != 0 && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
					{
						startTick += (playState.m_nMusicSpeed + tickDelay) * (rowDelay - 1);
					}
					if(!porta) memory.chnSettings[nChn].ticksToRender = 0;

					// Panning commands have to be re-applied after a note change with potential pan change.
					if(m.command == CMD_PANNING8
						|| ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && paramHi == 0x8)
						|| m.volcmd == VOLCMD_PANNING)
					{
						pChn->nPan = setPan;
					}

					if(m.command == CMD_OFFSET)
					{
						bool isExtended = false;
						SmpLength offset = CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn, &isExtended);
						if(!isExtended)
						{
							offset <<= 8;
							if(offset == 0) offset = pChn->oldOffset;
							offset += static_cast<SmpLength>(pChn->nOldHiOffset) << 16;
						}
						SampleOffset(*pChn, offset);
					} else if(m.command == CMD_OFFSETPERCENTAGE)
					{
						SampleOffset(*pChn, Util::muldiv_unsigned(pChn->nLength, m.param, 255));
					} else if(m.command == CMD_REVERSEOFFSET && pChn->pModSample != nullptr)
					{
						memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
						ReverseSampleOffset(*pChn, m.param);
						startTick = playState.m_nMusicSpeed - 1;
					} else if(m.volcmd == VOLCMD_OFFSET)
					{
						if(m.vol <= CountOf(pChn->pModSample->cues) && pChn->pModSample != nullptr)
						{
							SmpLength offset;
							if(m.vol == 0)
								offset = pChn->oldOffset;
							else
								offset = pChn->oldOffset = pChn->pModSample->cues[m.vol - 1];
							SampleOffset(*pChn, offset);
						}
					}
				}

				if(m.note == NOTE_KEYOFF || m.note == NOTE_NOTECUT || (m.note == NOTE_FADE && GetNumInstruments())
					|| ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xC0 && paramLo < numTicks)
					|| (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks))
				{
					stopNote = true;
				}

				if(m.command == CMD_VOLUME)
				{
					pChn->nVolume = m.param * 4;
				} else if(m.volcmd == VOLCMD_VOLUME)
				{
					pChn->nVolume = m.vol * 4;
				}

				if(pChn->pModSample && !stopNote)
				{
					// Check if we don't want to emulate some effect and thus stop processing.
					if(m.command < MAX_EFFECTS)
					{
						if(forbiddenCommands[m.command])
						{
							stopNote = true;
						} else if(m.command == CMD_MODCMDEX)
						{
							// Special case: Slides using extended commands
							switch(m.param & 0xF0)
							{
							case 0x10:
							case 0x20:
								stopNote = true;
							}
						}
					}

					if(m.volcmd < forbiddenVolCommands.size() && forbiddenVolCommands[m.volcmd])
					{
						stopNote = true;
					}
				}

				if(stopNote)
				{
					pChn->Stop();
					memory.chnSettings[nChn].ticksToRender = 0;
				} else
				{
					if(oldTickDuration != tickDuration && oldTickDuration != 0)
					{
						memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
					}

					switch(m.command)
					{
					case CMD_TONEPORTAVOL:
					case CMD_VOLUMESLIDE:
					case CMD_VIBRATOVOL:
						if(m.param || (GetType() != MOD_TYPE_MOD))
						{
							for(uint32 i = 0; i < numTicks; i++)
							{
								pChn->isFirstTick = (i == 0);
								VolumeSlide(pChn, m.param);
							}
						}
						break;

					case CMD_MODCMDEX:
						if((m.param & 0x0F) || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)))
						{
							pChn->isFirstTick = true;
							switch(m.param & 0xF0)
							{
							case 0xA0: FineVolumeUp(pChn, m.param & 0x0F, false); break;
							case 0xB0: FineVolumeDown(pChn, m.param & 0x0F, false); break;
							}
						}
						break;

					case CMD_S3MCMDEX:
						if(m.param == 0x9E)
						{
							// Play forward
							memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
							pChn->dwFlags.reset(CHN_PINGPONGFLAG);
						} else if(m.param == 0x9F)
						{
							// Reverse
							memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
							pChn->dwFlags.set(CHN_PINGPONGFLAG);
							if(!pChn->position.GetInt() && pChn->nLength && (m.IsNote() || !pChn->dwFlags[CHN_LOOP]))
							{
								pChn->position.Set(pChn->nLength - 1, SamplePosition::fractMax);
							}
						} else if((m.param & 0xF0) == 0x70)
						{
							// TODO
							//ExtendedS3MCommands(nChn, param);
						}
						break;
					}
					pChn->isFirstTick = true;
					switch(m.volcmd)
					{
					case VOLCMD_FINEVOLUP:		FineVolumeUp(pChn, m.vol, m_playBehaviour[kITVolColMemory]); break;
					case VOLCMD_FINEVOLDOWN:	FineVolumeDown(pChn, m.vol, m_playBehaviour[kITVolColMemory]); break;
					case VOLCMD_VOLSLIDEUP:
					case VOLCMD_VOLSLIDEDOWN:
						{
							// IT Compatibility: Volume column volume slides have their own memory
							// Test case: VolColMemory.it
							ModCommand::VOL vol = m.vol;
							if(vol == 0 && m_playBehaviour[kITVolColMemory])
							{
								vol = pChn->nOldVolParam;
								if(vol == 0)
									break;
							}
							if(m.volcmd == VOLCMD_VOLSLIDEUP)
								vol <<= 4;
							for(uint32 i = 0; i < numTicks; i++)
							{
								pChn->isFirstTick = (i == 0);
								VolumeSlide(pChn, vol);
							}
						}
						break;
					}

					if(porta)
					{
						// Portamento needs immediate syncing, as the pitch changes on each tick
						uint32 portaTick = memory.chnSettings[nChn].ticksToRender + startTick + 1;
						memory.chnSettings[nChn].ticksToRender += numTicks;
						memory.RenderChannel(nChn, tickDuration, portaTick);
					} else
					{
						memory.chnSettings[nChn].ticksToRender += (numTicks - startTick);
					}
				}
			}
		}
		oldTickDuration = tickDuration;

		// Pattern loop is not executed in FT2 if there are any position jump or pattern break commands on the same row.
		// Pattern loop is not executed in IT if there are any position jump commands on the same row.
		// Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm
		// Test case for IT: exception: LoopBreak.it
		if(patternLoopEndedOnThisRow
			&& (!m_playBehaviour[kFT2PatternLoopWithJumps] || !(positionJumpOnThisRow || patternBreakOnThisRow))
			&& (!m_playBehaviour[kITPatternLoopWithJumps] || !positionJumpOnThisRow))
		{
			std::map<double, int> startTimes;
			// This is really just a simple estimation for nested pattern loops. It should handle cases correctly where all parallel loops start and end on the same row.
			// If one of them starts or ends "in between", it will most likely calculate a wrong duration.
			// For S3M files, it's also way off.
			pChn = playState.Chn;
			for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++)
			{
				ModCommand::COMMAND command = pChn->rowCommand.command;
				ModCommand::PARAM param = pChn->rowCommand.param;
				if((command == CMD_S3MCMDEX && param >= 0xB1 && param <= 0xBF)
					|| (command == CMD_MODCMDEX && param >= 0x61 && param <= 0x6F))
				{
					const double start = memory.chnSettings[nChn].patLoop;
					if(!startTimes[start]) startTimes[start] = 1;
					startTimes[start] = mpt::lcm(startTimes[start], 1 + (param & 0x0F));
				}
			}
			for(const auto &i : startTimes)
			{
				memory.elapsedTime += (memory.elapsedTime - i.first) * (double)(i.second - 1);
				for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++)
				{
					if(memory.chnSettings[nChn].patLoop == i.first)
					{
						playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (i.second - 1);
						if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
						{
							memory.chnSettings[nChn].patLoop = memory.elapsedTime;
							memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
							memory.chnSettings[nChn].patLoopStart = playState.m_nRow + 1;
						}
						break;
					}
				}
			}
			if(GetType() == MOD_TYPE_IT)
			{
				// IT pattern loop start row update - at the end of a pattern loop, set pattern loop start to next row (for upcoming pattern loops with missing SB0)
				for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
				{
					if((pChn->rowCommand.command == CMD_S3MCMDEX && pChn->rowCommand.param >= 0xB1 && pChn->rowCommand.param <= 0xBF))
					{
						memory.chnSettings[nChn].patLoop = memory.elapsedTime;
						memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
					}
				}
			}
		}
	}

	// Now advance the sample positions for sample seeking on channels that are still playing
	if(adjustSamplePos)
	{
		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
		{
			if(memory.chnSettings[nChn].ticksToRender != GetLengthMemory::IGNORE_CHANNEL)
			{
				memory.RenderChannel(nChn, oldTickDuration);
			}
		}
	}

	if(retval.targetReached || target.mode == GetLengthTarget::NoTarget)
	{
		retval.lastOrder = playState.m_nCurrentOrder;
		retval.lastRow = playState.m_nRow;
	}
	retval.duration = memory.elapsedTime;
	results.push_back(retval);

	// Store final variables
	if(adjustMode & eAdjust)
	{
		if(retval.targetReached || target.mode == GetLengthTarget::NoTarget)
		{
			// Target found, or there is no target (i.e. play whole song)...
			m_PlayState = std::move(playState);
			m_PlayState.m_nNextRow = m_PlayState.m_nRow;
			m_PlayState.m_nFrameDelay = m_PlayState.m_nPatternDelay = 0;
			m_PlayState.m_nTickCount = Util::MaxValueOfType(m_PlayState.m_nTickCount) - 1;
			m_PlayState.m_bPositionChanged = true;
			for(CHANNELINDEX n = 0; n < GetNumChannels(); n++)
			{
				if(m_PlayState.Chn[n].nLastNote != NOTE_NONE)
				{
					m_PlayState.Chn[n].nNewNote = m_PlayState.Chn[n].nLastNote;
				}
				if(memory.chnSettings[n].vol != 0xFF && !adjustSamplePos)
				{
					m_PlayState.Chn[n].nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4;
				}
			}

#ifndef NO_PLUGINS
			// If there were any PC events, update plugin parameters to their latest value.
			std::bitset<MAX_MIXPLUGINS> plugSetProgram;
			for(const auto &param : memory.plugParams)
			{
				PLUGINDEX plug = param.first.first - 1;
				IMixPlugin *plugin = m_MixPlugins[plug].pMixPlugin;
				if(plugin != nullptr)
				{
					if(!plugSetProgram[plug])
					{
						// Used for bridged plugins to avoid sending out individual messages for each parameter.
						plugSetProgram.set(plug);
						plugin->BeginSetProgram();
					}
					plugin->SetParameter(param.first.second, param.second / PlugParamValue(ModCommand::maxColumnValue));
				}
			}
			if(plugSetProgram.any())
			{
				for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
				{
					if(plugSetProgram[i])
					{
						m_MixPlugins[i].pMixPlugin->EndSetProgram();
					}
				}
			}
#endif // NO_PLUGINS
		} else if(adjustMode != eAdjustOnSuccess)
		{
			// Target not found (e.g. when jumping to a hidden sub song), reset global variables...
			m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
			m_PlayState.m_nMusicTempo = m_nDefaultTempo;
			m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume;
		}
		// When adjusting the playback status, we will also want to update the visited rows vector according to the current position.
		if(sequence != Order.GetCurrentSequenceIndex())
		{
			Order.SetSequence(sequence);
		}
		visitedSongRows.Set(visitedRows);
	}

	return results;

}

[Fix] Possible out-of-bounds read when computing length of some IT files with pattern loops (OpenMPT: formats that are converted to IT, libopenmpt: IT/ITP/MO3), caught with afl-fuzz.

git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@10027 56274372-70c3-4bfc-bfc3-4c3a0b034d27


static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter)
{
#ifndef PB_ENABLE_MALLOC
    UNUSED(wire_type);
    UNUSED(iter);
    PB_RETURN_ERROR(stream, "no malloc support");
#else
    pb_type_t type;
    pb_decoder_t func;

    type = iter->pos->type;
    func = PB_DECODERS[PB_LTYPE(type)];

    switch (PB_HTYPE(type))
    {
        case PB_HTYPE_REQUIRED:
        case PB_HTYPE_OPTIONAL:
            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
                *(void**)iter->pData != NULL)
            {
                /* Duplicate field, have to release the old allocation first. */
                pb_release_single_field(iter);
            }

            if (PB_LTYPE(type) == PB_LTYPE_STRING ||
                PB_LTYPE(type) == PB_LTYPE_BYTES)
            {
                return func(stream, iter->pos, iter->pData);
            }
            else
            {
                if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1))
                    return false;

                initialize_pointer_field(*(void**)iter->pData, iter);
                return func(stream, iter->pos, *(void**)iter->pData);
            }

        case PB_HTYPE_REPEATED:
            if (wire_type == PB_WT_STRING
                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
            {
                /* Packed array, multiple items come in at once. */
                bool status = true;
                size_t *size = (size_t*)iter->pSize;
                size_t allocated_size = *size;
                void *pItem;
                pb_istream_t substream;

                if (!pb_make_string_substream(stream, &substream))
                    return false;

                while (substream.bytes_left)
                {
                    if (*size + 1 > allocated_size)
                    {
                        /* Allocate more storage. This tries to guess the
                         * number of remaining entries. Round the division
                         * upwards. */
                        allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;

                        if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
                        {
                            status = false;
                            break;
                        }
                    }

                    /* Decode the array entry */
                    pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size);
                    initialize_pointer_field(pItem, iter);
                    if (!func(&substream, iter->pos, pItem))
                    {
                        status = false;
                        break;
                    }
                    (*size)++;
                }
                pb_close_string_substream(stream, &substream);

                return status;
            }
            else
            {
                /* Normal repeated field, i.e. only one item at a time. */
                size_t *size = (size_t*)iter->pSize;
                void *pItem;

                (*size)++;
                if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
                    return false;

                pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size - 1);
                initialize_pointer_field(pItem, iter);
                return func(stream, iter->pos, pItem);
            }

        default:
            PB_RETURN_ERROR(stream, "invalid field type");
    }
#endif
}


  void operator()(OpKernelContext* context, const Tensor& x_input,
                  const Tensor& scale_input, const Tensor& offset_input,
                  const Tensor& estimated_mean_input,
                  const Tensor& estimated_variance_input,
                  const Tensor* side_input, U epsilon, U exponential_avg_factor,
                  FusedBatchNormActivationMode activation_mode,
                  Tensor* y_output, Tensor* batch_mean_output,
                  Tensor* batch_var_output, Tensor* saved_mean_output,
                  Tensor* saved_var_output, TensorFormat tensor_format,
                  bool use_reserved_space) {
    OP_REQUIRES(context, side_input == nullptr,
                errors::Internal(
                    "The CPU implementation of FusedBatchNorm does not support "
                    "side input."));
    OP_REQUIRES(context,
                activation_mode == FusedBatchNormActivationMode::kIdentity,
                errors::Internal("The CPU implementation of FusedBatchNorm "
                                 "does not support activations."));

    if (use_reserved_space) {
      Tensor* dummy_reserve_space = nullptr;
      OP_REQUIRES_OK(context,
                     context->allocate_output(5, {}, &dummy_reserve_space));
      // Initialize the memory, to avoid sanitizer alerts.
      dummy_reserve_space->flat<U>()(0) = U();
    }
    Tensor transformed_x;
    Tensor transformed_y;
    if (tensor_format == FORMAT_NCHW) {
      const int64 in_batch = GetTensorDim(x_input, tensor_format, 'N');
      const int64 in_rows = GetTensorDim(x_input, tensor_format, 'H');
      const int64 in_cols = GetTensorDim(x_input, tensor_format, 'W');
      const int64 in_depths = GetTensorDim(x_input, tensor_format, 'C');
      OP_REQUIRES_OK(context, context->allocate_temp(
                                  DataTypeToEnum<T>::value,
                                  ShapeFromFormat(FORMAT_NHWC, in_batch,
                                                  in_rows, in_cols, in_depths),
                                  &transformed_x));
      OP_REQUIRES_OK(context, context->allocate_temp(
                                  DataTypeToEnum<T>::value,
                                  ShapeFromFormat(FORMAT_NHWC, in_batch,
                                                  in_rows, in_cols, in_depths),
                                  &transformed_y));
      // Perform NCHW to NHWC
      std::vector<int32> perm = {0, 2, 3, 1};
      OP_REQUIRES_OK(
          context, ::tensorflow::DoTranspose(context->eigen_device<CPUDevice>(),
                                             x_input, perm, &transformed_x));
    } else {
      transformed_x = x_input;
      transformed_y = *y_output;
    }
    typename TTypes<T, 4>::Tensor x(transformed_x.tensor<T, 4>());
    typename TTypes<U>::ConstVec scale(scale_input.vec<U>());
    typename TTypes<U>::ConstVec offset(offset_input.vec<U>());
    typename TTypes<U>::ConstVec estimated_mean(estimated_mean_input.vec<U>());
    typename TTypes<U>::ConstVec estimated_variance(
        estimated_variance_input.vec<U>());
    typename TTypes<T, 4>::Tensor y(transformed_y.tensor<T, 4>());
    typename TTypes<U>::Vec batch_mean(batch_mean_output->vec<U>());
    typename TTypes<U>::Vec batch_variance(batch_var_output->vec<U>());

    const CPUDevice& d = context->eigen_device<CPUDevice>();

    const int depth = x.dimension(3);
    const int size = x.size();
    const int rest_size = size / depth;
    Eigen::DSizes<Eigen::Index, 2> rest_by_depth(rest_size, depth);

#if !defined(EIGEN_HAS_INDEX_LIST)
    Eigen::DSizes<Eigen::Index, 2> one_by_depth(1, depth);
    Eigen::array<int, 1> reduce_dims({0});
    Eigen::array<int, 2> bcast_spec({rest_size, 1});
#else
    Eigen::IndexList<Eigen::type2index<1>, Eigen::Index> one_by_depth;
    one_by_depth.set(1, depth);
    Eigen::IndexList<Eigen::Index, Eigen::type2index<1>> bcast_spec;
    bcast_spec.set(0, rest_size);
#endif

    auto x_rest_by_depth = x.reshape(rest_by_depth).template cast<U>();
    auto x_centered =
        x_rest_by_depth -
        estimated_mean.reshape(one_by_depth).broadcast(bcast_spec);
    auto scaling_factor = ((estimated_variance + epsilon).rsqrt() * scale)
                              .eval()
                              .reshape(one_by_depth)
                              .broadcast(bcast_spec);
    auto x_scaled = x_centered * scaling_factor;
    auto x_shifted =
        (x_scaled + offset.reshape(one_by_depth).broadcast(bcast_spec))
            .template cast<T>();

    y.reshape(rest_by_depth).device(d) = x_shifted;
    batch_mean.device(d) = estimated_mean;
    batch_variance.device(d) = estimated_variance;

    if (tensor_format == FORMAT_NCHW) {
      // Perform NHWC to NCHW
      const std::vector<int32> perm = {0, 3, 1, 2};
      const Status s = ::tensorflow::DoTranspose(
          context->eigen_device<CPUDevice>(), transformed_y, perm, y_output);
      if (!s.ok()) {
        context->SetStatus(errors::InvalidArgument("Transpose failed: ", s));
      }
    }
  }

Fix FPE issue with `tf.raw_ops.FusedBatchNorm`.

PiperOrigin-RevId: 370948185
Change-Id: If0c8e0320062ed6363e94ff5fe38e6a301f69ac2

#define EIGEN_USE_THREADS

#define EIGEN_USE_GPU


GF_Err flac_dmx_process(GF_Filter *filter)
{
	GF_FLACDmxCtx *ctx = gf_filter_get_udta(filter);
	GF_FilterPacket *pck, *dst_pck;
	u8 *output;
	u8 *start;
	Bool final_flush=GF_FALSE;
	u32 pck_size, remain, prev_pck_size;
	u64 cts = GF_FILTER_NO_TS;
	FLACHeader hdr;

	//always reparse duration
	if (!ctx->duration.num)
		flac_dmx_check_dur(filter, ctx);

	if (ctx->opid && !ctx->is_playing)
		return GF_OK;

	pck = gf_filter_pid_get_packet(ctx->ipid);
	if (!pck) {
		if (gf_filter_pid_is_eos(ctx->ipid)) {
			if (!ctx->flac_buffer_size) {
				if (ctx->opid)
					gf_filter_pid_set_eos(ctx->opid);
				if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
				ctx->src_pck = NULL;
				return GF_EOS;
			}
			final_flush = GF_TRUE;
		} else {
			return GF_OK;
		}
	}

	prev_pck_size = ctx->flac_buffer_size;
	if (pck && !ctx->resume_from) {
		u8 *data = (u8 *) gf_filter_pck_get_data(pck, &pck_size);

		if (ctx->byte_offset != GF_FILTER_NO_BO) {
			u64 byte_offset = gf_filter_pck_get_byte_offset(pck);
			if (!ctx->flac_buffer_size) {
				ctx->byte_offset = byte_offset;
			} else if (ctx->byte_offset + ctx->flac_buffer_size != byte_offset) {
				ctx->byte_offset = GF_FILTER_NO_BO;
				if ((byte_offset != GF_FILTER_NO_BO) && (byte_offset>ctx->flac_buffer_size) ) {
					ctx->byte_offset = byte_offset - ctx->flac_buffer_size;
				}
			}
		}

		if (ctx->flac_buffer_size + pck_size > ctx->flac_buffer_alloc) {
			ctx->flac_buffer_alloc = ctx->flac_buffer_size + pck_size;
			ctx->flac_buffer = gf_realloc(ctx->flac_buffer, ctx->flac_buffer_alloc);
		}
		memcpy(ctx->flac_buffer + ctx->flac_buffer_size, data, pck_size);
		ctx->flac_buffer_size += pck_size;
	}

	//input pid sets some timescale - we flushed pending data , update cts
	if (ctx->timescale && pck) {
		cts = gf_filter_pck_get_cts(pck);
	}

	if (cts == GF_FILTER_NO_TS) {
		//avoids updating cts
		prev_pck_size = 0;
	}

	remain = ctx->flac_buffer_size;
	start = ctx->flac_buffer;

	if (ctx->resume_from) {
		start += ctx->resume_from - 1;
		remain -= ctx->resume_from - 1;
		ctx->resume_from = 0;
	}

	while (remain>2) {
		u32 next_frame=0, nb_samp;
		u32 cur_size = remain-2;
		u8 *cur_buf = start+2;
		u8 *hdr_start = NULL;

		if (final_flush) {
			next_frame = remain;
		} else {
			while (cur_size) {
				//wait till we have a frame header
				hdr_start = memchr(cur_buf, 0xFF, cur_size);
				if (!hdr_start) break;
				next_frame = (u32) (hdr_start-start);
				if (next_frame == remain)
					break;

				if ((hdr_start[1]&0xFC) == 0xF8) {
					if (flac_parse_header(ctx, hdr_start, (u32) remain - next_frame, &hdr))
						break;
				}
				cur_buf = hdr_start+1;
				cur_size = (u32) (cur_buf - start);
				assert(cur_size<=remain);
				cur_size = remain - cur_size;
				hdr_start = NULL;
			}
			if (!hdr_start) break;
			if (next_frame == remain)
				break;
		}

		if (!ctx->initialized) {
			u32 size = next_frame;
			u32 dsi_end = 0;
			//we have a header
			gf_bs_reassign_buffer(ctx->bs, ctx->flac_buffer, size);
			u32 magic = gf_bs_read_u32(ctx->bs);
			if (magic != GF_4CC('f','L','a','C')) {

			}
			while (gf_bs_available(ctx->bs)) {
				Bool last = gf_bs_read_int(ctx->bs, 1);
				u32 type = gf_bs_read_int(ctx->bs, 7);
				u32 len = gf_bs_read_int(ctx->bs, 24);

				if (type==0) {
					u16 min_block_size = gf_bs_read_u16(ctx->bs);
					u16 max_block_size = gf_bs_read_u16(ctx->bs);
					/*u32 min_frame_size = */gf_bs_read_u24(ctx->bs);
					/*u32 max_frame_size = */gf_bs_read_u24(ctx->bs);
					ctx->sample_rate = gf_bs_read_int(ctx->bs, 20);
					ctx->nb_channels = 1 + gf_bs_read_int(ctx->bs, 3);
					ctx->bits_per_sample = 1 + gf_bs_read_int(ctx->bs, 5);
					if (min_block_size==max_block_size) ctx->block_size = min_block_size;
					else ctx->block_size = 0;

					ctx->duration.num = gf_bs_read_long_int(ctx->bs, 36);
					ctx->duration.den = ctx->sample_rate;
					//ignore the rest
					gf_bs_skip_bytes(ctx->bs, 16);
					dsi_end = (u32) gf_bs_get_position(ctx->bs);

				} else {
					//ignore the rest for now
					//TODO: expose metadata, pictures and co
					gf_bs_skip_bytes(ctx->bs, len);
				}
				if (last) break;
			}
			flac_dmx_check_pid(filter, ctx, ctx->flac_buffer+4, dsi_end-4);
			remain -= size;
			start += size;
			ctx->initialized = GF_TRUE;
			if (!ctx->is_playing) break;
			continue;
		}

		//we have a next frame, check we are synchronize
		if ((start[0] != 0xFF) && ((start[1]&0xFC) != 0xF8)) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FLACDmx] invalid frame, droping %d bytes and resyncing\n", next_frame));
			start += next_frame;
			remain -= next_frame;
			continue;
		}

		flac_parse_header(ctx,start, next_frame, &hdr);
		if (hdr.sample_rate != ctx->sample_rate) {
			ctx->sample_rate = hdr.sample_rate;
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(ctx->sample_rate));
		}

		nb_samp = hdr.block_size;

		if (ctx->in_seek) {
			u64 nb_samples_at_seek = (u64) (ctx->start_range * ctx->sample_rate);
			if (ctx->cts + nb_samp >= nb_samples_at_seek) {
				//u32 samples_to_discard = (ctx->cts + nb_samp ) - nb_samples_at_seek;
				ctx->in_seek = GF_FALSE;
			}
		}

		if (ctx->timescale && !prev_pck_size && (cts != GF_FILTER_NO_TS) ) {
			ctx->cts = cts;
			cts = GF_FILTER_NO_TS;
		}

		if (!ctx->in_seek) {
			dst_pck = gf_filter_pck_new_alloc(ctx->opid, next_frame, &output);
			memcpy(output, start, next_frame);

			gf_filter_pck_set_cts(dst_pck, ctx->cts);
			if (!ctx->timescale || (ctx->timescale==ctx->sample_rate) )
				gf_filter_pck_set_duration(dst_pck, nb_samp);
			else {
				gf_filter_pck_set_duration(dst_pck, (nb_samp * ctx->timescale) / ctx->sample_rate);
			}
			gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
			gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);

			if (ctx->byte_offset != GF_FILTER_NO_BO) {
				gf_filter_pck_set_byte_offset(dst_pck, ctx->byte_offset);
			}
			gf_filter_pck_send(dst_pck);
		}
		flac_dmx_update_cts(ctx, nb_samp);

		assert (start[0] == 0xFF);
		assert((start[1]&0xFC) == 0xF8);

		start += next_frame;
		assert(remain >= next_frame);
		remain -= next_frame;

	}

	if (!pck) {
		ctx->flac_buffer_size = 0;
		return flac_dmx_process(filter);
	} else {
		if (remain < ctx->flac_buffer_size) {
			memmove(ctx->flac_buffer, start, remain);
		}
		ctx->flac_buffer_size = remain;
		gf_filter_pid_drop_packet(ctx->ipid);
	}
	return GF_OK;
}

fixed 1718

typedef struct
{
	u64 pos;
	Double duration;
} FLACIdx;

typedef struct
{
	u32 block_size;
	u32 sample_rate;
} FLACHeader;

typedef struct
{
	//filter args
	Double index;

	//only one input pid declared
	GF_FilterPid *ipid;
	//only one output pid declared
	GF_FilterPid *opid;

	GF_BitStream *bs;
	u64 file_pos, cts, prev_cts;

	GF_Fraction64 duration;
	Double start_range;
	Bool in_seek;
	u32 timescale;
	Bool is_playing;
	Bool is_file;
	Bool initial_play_done, file_loaded;

	Bool initialized;
	u32 sample_rate, nb_channels, bits_per_sample, block_size;

	u8 *flac_buffer;
	u32 flac_buffer_size, flac_buffer_alloc, resume_from;
	u64 byte_offset;

	GF_FilterPacket *src_pck;

	Bool recompute_cts;
	FLACIdx *indexes;
	u32 index_alloc_size, index_size;
	u32 bitrate;
} GF_FLACDmxCtx;

static u32 flac_dmx_block_sizes[] =
{
	0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384,  32768
};

static u32 flac_dmx_samplerates[] =
{
	0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000
};

static const GF_FilterCapability FLACDmxCaps[] =
{
	CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
	CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "flac"),
	CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/flac"),
	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_FLAC),
	CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
	{0},
	CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
	CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
	CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_FLAC),
	CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
};

#define OFFS(_n)	#_n, offsetof(GF_FLACDmxCtx, _n)

static const GF_FilterArgs FLACDmxArgs[] =
{
	{ OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
	{0}
};