private static <T> void append_cellValue(
      final DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      final CellSetter<T>[] setters,
      ClassWriter cw,
      final String classType,
      final int maxMethodSize) {

    ShardingHelper.shard(
        setters.length,
        maxMethodSize,
        new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize) {
          @Override
          protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
            Label defaultLabel = new Label();
            Label[] labels = newLabels(end - start);
            mv.visitTableSwitchInsn(
                delayedCellSetters.length + start,
                delayedCellSetters.length + end - 1,
                defaultLabel,
                labels);

            for (int i = start; i < end; i++) {

              mv.visitLabel(labels[i - start]);
              mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
              if (setters[i] != null) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, classType, "setter" + i, "L" + CELL_SETTER_TYPE + ";");
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, classType, "currentInstance", "Ljava/lang/Object;");
                mv.visitVarInsn(ALOAD, 1);
                mv.visitVarInsn(ILOAD, 2);
                mv.visitVarInsn(ILOAD, 3);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(
                    GETFIELD,
                    classType,
                    "parsingContext",
                    AsmUtils.toDeclaredLType(ParsingContext.class));
                mv.visitMethodInsn(
                    INVOKEINTERFACE,
                    CELL_SETTER_TYPE,
                    "set",
                    "(Ljava/lang/Object;[CIIL" + AsmUtils.toType(ParsingContext.class) + ";)V",
                    true);
              }
              if (i < (end - 1)) {
                mv.visitJumpInsn(GOTO, defaultLabel);
              }
            }
            mv.visitLabel(defaultLabel);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
          }

          @Override
          protected int maxArgIndex() {
            return 4;
          }

          @Override
          protected void loadArguments(MethodVisitor mv) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitVarInsn(ILOAD, 2);
            mv.visitVarInsn(ILOAD, 3);
            mv.visitVarInsn(ILOAD, 4);
          }

          @Override
          protected int argIndex() {
            return 4;
          }

          @Override
          protected String name() {
            return "_cellValue";
          }

          @Override
          protected String signature() {
            return "([CIII)V";
          }

          @Override
          protected int leafStart() {
            return delayedCellSetters.length;
          }

          @Override
          protected void appendDebugInfo(MethodVisitor mv, Label startLabel, Label endLabel) {
            mv.visitLocalVariable(
                "this",
                "Lorg/sfm/reflect/asm/sample/AsmCsvMapperCellHandler;",
                null,
                startLabel,
                endLabel,
                0);
            mv.visitLocalVariable("chars", "[C", null, startLabel, endLabel, 1);
            mv.visitLocalVariable("offset", "I", null, startLabel, endLabel, 2);
            mv.visitLocalVariable("length", "I", null, startLabel, endLabel, 3);
            mv.visitLocalVariable("cellIndex", "I", null, startLabel, endLabel, 4);
          }
        });
  }
  private static <T> void appendInit(
      DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      CellSetter<T>[] setters,
      ClassWriter cw,
      String targetType,
      String classType,
      int maxSize) {
    MethodVisitor mv; // constructor
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "<init>",
              "("
                  + AsmUtils.toDeclaredLType(Instantiator.class)
                  + AsmUtils.toDeclaredLType(DelayedCellSetter[].class)
                  + AsmUtils.toDeclaredLType(CellSetter[].class)
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContext.class)
                  + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
                  + ")V",
              "("
                  + "L"
                  + AsmUtils.toType(Instantiator.class)
                  + "<L"
                  + AsmUtils.toType(CsvMapperCellHandler.class)
                  + "<L"
                  + targetType
                  + ";>;L"
                  + targetType
                  + ";>;"
                  + "[L"
                  + DELAYED_CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";*>;"
                  + "[L"
                  + CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";>;"
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContext.class)
                  + "L"
                  + AsmUtils.toType(FieldMapperErrorHandler.class)
                  + "<L"
                  + AsmUtils.toType(CsvColumnKey.class)
                  + ";>;"
                  + ")V",
              null);
      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 4);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitInsn(ARRAYLENGTH);
      mv.visitVarInsn(ALOAD, 3);
      mv.visitInsn(ARRAYLENGTH);
      mv.visitVarInsn(ALOAD, 5);
      mv.visitVarInsn(ALOAD, 6);
      mv.visitMethodInsn(
          INVOKESPECIAL,
          CSV_CELL_MAPPER_TYPE,
          "<init>",
          "("
              + AsmUtils.toDeclaredLType(Instantiator.class)
              + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
              + "I"
              + "I"
              + AsmUtils.toDeclaredLType(ParsingContext.class)
              + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
              + ")V",
          false);

      ShardingHelper.shard(
          delayedCellSetters.length,
          maxSize,
          new ShardingHelper.ShardCallBack() {
            @Override
            public void leafDispatch(String suffix, int start, int end) {}

            @Override
            public void nodeDispatch(String suffix, int divide, int start, int end) {}
          });
      for (int i = 0; i < delayedCellSetters.length; i++) {
        if (delayedCellSetters[i] != null) {
          mv.visitVarInsn(ALOAD, 0);
          mv.visitVarInsn(ALOAD, 2);
          AsmUtils.addIndex(mv, i);
          mv.visitInsn(AALOAD);
          mv.visitFieldInsn(
              PUTFIELD,
              classType,
              "delayedCellSetter" + i,
              AsmUtils.toDeclaredLType(DELAYED_CELL_SETTER_TYPE));
        }
      }

      for (int i = 0; i < setters.length; i++) {
        if (setters[i] != null) {
          mv.visitVarInsn(ALOAD, 0);
          mv.visitVarInsn(ALOAD, 3);
          AsmUtils.addIndex(mv, i);
          mv.visitInsn(AALOAD);
          mv.visitFieldInsn(
              PUTFIELD, classType, "setter" + i, AsmUtils.toDeclaredLType(CELL_SETTER_TYPE));
        }
      }

      mv.visitInsn(RETURN);
      mv.visitMaxs(7, 7);
      mv.visitEnd();
    }
  }
  private static <T> void append_delayedCellValue(
      final DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      final ClassWriter cw,
      final String classType,
      final int maxMethodSize) {

    ShardingHelper.shard(
        delayedCellSetters.length,
        maxMethodSize,
        new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize) {
          @Override
          protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
            Label defaultLabel = new Label();
            Label[] labels = newLabels(end - start);
            mv.visitTableSwitchInsn(start, end - 1, defaultLabel, labels);

            for (int i = start; i < end; i++) {
              mv.visitLabel(labels[i - start]);
              mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
              if (delayedCellSetters[i] != null) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(
                    GETFIELD,
                    classType,
                    "delayedCellSetter" + i,
                    "L" + DELAYED_CELL_SETTER_TYPE + ";");
                mv.visitVarInsn(ALOAD, 1);
                mv.visitVarInsn(ILOAD, 2);
                mv.visitVarInsn(ILOAD, 3);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(
                    GETFIELD,
                    classType,
                    "parsingContext",
                    AsmUtils.toDeclaredLType(ParsingContext.class));
                mv.visitMethodInsn(
                    INVOKEINTERFACE,
                    DELAYED_CELL_SETTER_TYPE,
                    "set",
                    "([CIIL" + AsmUtils.toType(ParsingContext.class) + ";)V",
                    true);
              }
              if (i < (end - 1)) {
                mv.visitJumpInsn(GOTO, defaultLabel);
              }
            }
            mv.visitLabel(defaultLabel);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
          }

          @Override
          protected void loadArguments(MethodVisitor mv) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitVarInsn(ILOAD, 2);
            mv.visitVarInsn(ILOAD, 3);
            mv.visitVarInsn(ILOAD, 4);
          }

          @Override
          protected int maxArgIndex() {
            return 4;
          }

          @Override
          protected int argIndex() {
            return 4;
          }

          @Override
          protected String name() {
            return "_delayedCellValue";
          }

          @Override
          protected String signature() {
            return "([CIII)V";
          }
        });
  }
  private static <T> void appendApplyDelayedSetter(
      final DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      final boolean ignoreException,
      final ClassWriter cw,
      final String classType,
      final int maxMethodSize) {
    ShardingHelper.shard(
        delayedCellSetters.length,
        maxMethodSize,
        new ShardingHelper.ShardCallBack() {
          @Override
          public void leafDispatch(String suffix, int start, int end) {
            MethodVisitor mv;
            mv = cw.visitMethod(ACC_PUBLIC, "applyDelayedSetters" + suffix, "()V", null, null);
            mv.visitCode();

            if (end - start > 0) {

              if (!ignoreException) {
                Label[] labels =
                    newLabels(
                        (3 * getNbNonNullSettersWithSetters(delayedCellSetters, start, end)) + 1);
                for (int i = start, j = 0; i < end; i++) {
                  if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
                    mv.visitTryCatchBlock(
                        labels[j], labels[j + 1], labels[j + 2], "java/lang/Exception");
                    j += 3;
                  }
                }

                for (int i = start, j = 0; i < end; i++) {
                  if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
                    mv.visitLabel(labels[j]);
                    if (j > 0) {
                      mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
                    }
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitMethodInsn(
                        INVOKESPECIAL, classType, "applyDelayedCellSetter" + i, "()V", false);
                    mv.visitLabel(labels[j + 1]);
                    mv.visitJumpInsn(GOTO, labels[j + 3]);
                    mv.visitLabel(labels[j + 2]);
                    mv.visitFrame(
                        Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Exception"});
                    mv.visitVarInsn(ASTORE, 1);
                    mv.visitVarInsn(ALOAD, 0);
                    AsmUtils.addIndex(mv, i);
                    mv.visitVarInsn(ALOAD, 1);
                    mv.visitMethodInsn(
                        INVOKEVIRTUAL, classType, "fieldError", "(ILjava/lang/Exception;)V", false);

                    j += 3;
                  }
                }
                mv.visitLabel(labels[labels.length - 1]);
                mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
              } else {
                for (int i = start; i < end; i++) {
                  if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitMethodInsn(
                        INVOKESPECIAL, classType, "applyDelayedCellSetter" + i, "()V", false);
                  }
                }
              }
            }
            mv.visitInsn(RETURN);
            mv.visitMaxs(3, 2);
            mv.visitEnd();
          }

          @Override
          public void nodeDispatch(String suffix, int divide, int start, int end) {
            MethodVisitor mv;
            mv =
                cw.visitMethod(
                    ACC_PUBLIC,
                    "applyDelayedSetters" + suffix,
                    "()V",
                    null,
                    new String[] {"java/lang/Exception"});
            mv.visitCode();

            final int powerOfTwo = Integer.numberOfTrailingZeros(divide);

            int sStart = start >> powerOfTwo;
            int sEnd = end >> powerOfTwo;

            int left = end - (sEnd << powerOfTwo);
            if (left > 0) {
              sEnd++;
            }

            for (int i = sStart; i < sEnd; i++) {

              int estart = i * divide;
              int eend = Math.min(end, (i + 1) * divide);

              mv.visitVarInsn(ALOAD, 0);
              mv.visitMethodInsn(
                  INVOKESPECIAL,
                  classType,
                  "applyDelayedSetters" + (divide / maxMethodSize) + "n" + estart + "t" + eend,
                  "()V",
                  false);
            }

            mv.visitInsn(RETURN);
            mv.visitMaxs(6, 5);
            mv.visitEnd();
          }
        });
  }
  private static <T> void appendPeekDelayedCellSetterValue(
      final DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      final ClassWriter cw,
      final String classType,
      final int maxMethodSize) {

    ShardingHelper.shard(
        delayedCellSetters.length,
        maxMethodSize,
        new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize) {
          @Override
          protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
            Label defaultLabel = new Label();
            Label[] labels = newLabels(end - start);
            mv.visitTableSwitchInsn(start, end - 1, defaultLabel, labels);

            for (int i = start; i < end; i++) {
              mv.visitLabel(labels[i - start]);
              mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
              if (delayedCellSetters[i] != null) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(
                    GETFIELD,
                    classType,
                    "delayedCellSetter" + i,
                    "L" + DELAYED_CELL_SETTER_TYPE + ";");
                mv.visitMethodInsn(
                    INVOKEINTERFACE,
                    DELAYED_CELL_SETTER_TYPE,
                    "peekValue",
                    "()Ljava/lang/Object;",
                    true);
                mv.visitInsn(ARETURN);
              } else if (i < (delayedCellSetters.length - 1)) {
                mv.visitInsn(ACONST_NULL);
                mv.visitInsn(ARETURN);
              }
            }
            mv.visitLabel(defaultLabel);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
          }

          @Override
          protected int maxArgIndex() {
            return 1;
          }

          @Override
          protected void loadArguments(MethodVisitor mv) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ILOAD, 1);
          }

          @Override
          protected int argIndex() {
            return 1;
          }

          @Override
          protected String name() {
            return "_peekDelayedCellSetterValue";
          }

          @Override
          protected String signature() {
            return "(I)Ljava/lang/Object;";
          }
        });

    MethodVisitor mv;
    mv =
        cw.visitMethod(
            ACC_PUBLIC,
            "peekDelayedCellSetterValue",
            "(L" + AsmUtils.toType(CsvColumnKey.class) + ";)Ljava/lang/Object;",
            null,
            null);
    mv.visitCode();

    mv.visitVarInsn(ALOAD, 1);
    mv.visitMethodInsn(
        INVOKEVIRTUAL, AsmUtils.toType(CsvColumnKey.class), "getIndex", "()I", false);
    mv.visitVarInsn(ISTORE, 2);
    mv.visitVarInsn(ALOAD, 0);
    mv.visitVarInsn(ILOAD, 2);
    mv.visitMethodInsn(
        INVOKESPECIAL, classType, "_peekDelayedCellSetterValue", "(I)Ljava/lang/Object;", false);

    mv.visitInsn(ARETURN);
    mv.visitMaxs(1, 2);
    mv.visitEnd();
  }