TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  TF_LITE_ENSURE(context, input != nullptr);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
  TF_LITE_ENSURE(context, output != nullptr);

  TF_LITE_ENSURE(context, input != nullptr);
  TF_LITE_ENSURE(context, output != nullptr);
  TF_LITE_ENSURE_EQ(context, 1, output->dims->data[0]);
  TF_LITE_ENSURE_EQ(context, 1, input->dims->data[0]);
  TF_LITE_ENSURE_EQ(context, 1, input->dims->data[1]);
  TF_LITE_ENSURE_EQ(context, 1, output->dims->data[2]);
  TF_LITE_ENSURE_EQ(context, 1, input->dims->data[2]);
  TF_LITE_ENSURE_EQ(context, output->dims->data[3], input->dims->data[3]);

  TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);

  // The circular buffer custom operator currently only supports int8_t.
  TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteInt8);

  // TODO(b/132070898): Use statically slotted OpData structures until a
  // scratch memory API is ready.
  TFLITE_DCHECK_LE(op_data_counter, kMaxOpDataSize);
  OpData* op_data = &op_data_array[op_data_counter++];
  // The last circular buffer layer (length 5) simply accumulates outputs, and
  // does not run periodically.
  // TODO(b/150001379): Move this special case logic to the tflite flatbuffer.
  if (output->dims->data[1] == 5) {
    op_data->cycles_max = 1;
  } else {
    op_data->cycles_max = 2;
  }
  op_data->cycles_until_run = op_data->cycles_max;
  node->user_data = op_data;

  return kTfLiteOk;
}
The software reads data past the end, or before the beginning, of the intended buffer.