gt:             fillEmpty(n, nc, records, list, data);
context: /*
   * This material is distributed under the GNU General Public License
   * Version 2. You may review the terms of this license at
   * http://www.gnu.org/licenses/gpl-2.0.html
   *
   * Copyright (c) 1995, 1996, Robert Gentleman and Ross Ihaka
   * Copyright (c) 1998-2013, The R Core Team
   * Copyright (c) 2014, 2017, Oracle and/or its affiliates
   *
   * All rights reserved.
   */
  
  package com.oracle.truffle.r.nodes.builtin.base;
  
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.charAt0;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.emptyStringVector;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.length;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lengthLte;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lt;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
  import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
  import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
  import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  
  import com.oracle.truffle.api.CompilerDirectives;
  import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
  import com.oracle.truffle.api.dsl.Specialization;
  import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
  import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
  import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
  import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
  import com.oracle.truffle.r.runtime.RError;
  import com.oracle.truffle.r.runtime.RError.Message;
  import com.oracle.truffle.r.runtime.RInternalError;
  import com.oracle.truffle.r.runtime.RRuntime;
  import com.oracle.truffle.r.runtime.builtins.RBuiltin;
  import com.oracle.truffle.r.runtime.conn.RConnection;
  import com.oracle.truffle.r.runtime.conn.StdConnections;
  import com.oracle.truffle.r.runtime.data.RComplex;
  import com.oracle.truffle.r.runtime.data.RDataFactory;
  import com.oracle.truffle.r.runtime.data.RFunction;
  import com.oracle.truffle.r.runtime.data.RList;
  import com.oracle.truffle.r.runtime.data.RNull;
  import com.oracle.truffle.r.runtime.data.RVector;
  import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
  import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
  import com.oracle.truffle.r.runtime.ops.na.NACheck;
  
  @RBuiltin(name = "scan", kind = INTERNAL, parameterNames = {"file", "what", "nmax", "sep", "dec", "quote", "skip", "nlines", "na.strings", "flush", "fill", "strip.white", "quiet", "blank.lines.skip",
                  "multi.line", "comment.char", "allowEscapes", "encoding", "skipNull"}, behavior = IO)
  public abstract class Scan extends RBuiltinNode.Arg19 {
  
      private static final int SCAN_BLOCKSIZE = 1000;
      private static final int NO_COMCHAR = 100000; /* won't occur even in Unicode */
  
      private final NACheck naCheck = NACheck.create();
      @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
  
      @Child private CastToVectorNode castVector;
  
      private RAbstractVector castVector(Object value) {
          if (castVector == null) {
              CompilerDirectives.transferToInterpreterAndInvalidate();
              castVector = insert(CastToVectorNodeGen.create(false));
          }
          return ((RAbstractVector) castVector.doCast(value)).materialize();
      }
  
      @SuppressWarnings("unused")
      private static class LocalData {
          final HashMap<String, String> stringTable = new HashMap<>();
          RAbstractStringVector naStrings = null;
          boolean quiet = false;
          char sepchar = 0; // 0 means any whitespace
          char decchar = '.';
          char[] quoteset = new char[0];
          int comchar = NO_COMCHAR;
          // connection-related (currently not supported)
          // int ttyflag = 0;
          RConnection con = null;
          // connection-related (currently not supported)
          // boolean wasopen = false;
          boolean escapes = false;
          int save = 0;
          boolean isLatin1 = false;
          boolean isUTF8 = false;
          boolean atStart = false;
          boolean embedWarn = false;
          boolean skipNull = false;
      }
  
      static {
          Casts casts = new Casts(Scan.class);
          casts.arg("file").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
  
          casts.arg("what").mustNotBeMissing().mustBe(nullValue().not()).mustBe(instanceOf(RFunction.class).not()).asVector();
  
          casts.arg("nmax").asIntegerVector().findFirst(0).replaceNA(0).mapIf(lt(0), constant(0));
  
          casts.arg("sep").mapNull(emptyStringVector()).mustBe(stringValue()).asStringVector().findFirst("").mustBe(lengthLte(1), RError.Message.MUST_BE_ONE_BYTE, "'sep' value");
  
          casts.arg("dec").defaultError(RError.Message.INVALID_DECIMAL_SEP).mapNull(constant(".")).mustBe(stringValue()).asStringVector().findFirst(".").mustBe(length(1),
                          RError.Message.MUST_BE_ONE_BYTE, "'sep' value");
  
          casts.arg("quote").defaultError(RError.Message.INVALID_QUOTE_SYMBOL).mapNull(constant("")).mustBe(stringValue()).asStringVector().findFirst("");
  
          casts.arg("skip").asIntegerVector().findFirst(0).replaceNA(0).mapIf(lt(0), constant(0));
  
          casts.arg("nlines").asIntegerVector().findFirst(0).replaceNA(0).mapIf(lt(0), constant(0));
  
          casts.arg("na.strings").mustBe(stringValue());
  
          casts.arg("flush").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean());
  
          casts.arg("fill").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean());
  
          casts.arg("strip.white").mustBe(logicalValue());
  
          casts.arg("quiet").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).replaceNA(RRuntime.LOGICAL_FALSE).map(toBoolean());
  
          casts.arg("blank.lines.skip").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).replaceNA(RRuntime.LOGICAL_TRUE).map(toBoolean());
  
          casts.arg("multi.line").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).replaceNA(RRuntime.LOGICAL_TRUE).map(toBoolean());
  
          casts.arg("comment.char").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst().mustBe(lengthLte(1)).map(charAt0(RRuntime.INT_NA)).replaceNA(NO_COMCHAR);
  
          casts.arg("allowEscapes").asLogicalVector().findFirst().mustNotBeNA().map(toBoolean());
  
          casts.arg("encoding").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
  
          casts.arg("skipNull").asLogicalVector().findFirst().mustNotBeNA().map(toBoolean());
  
      }
  
      @Specialization
      @TruffleBoundary
      protected Object doScan(int file, RAbstractVector what, int nmax, String sep, String dec, String quotes, int nskip,
                      int nlines, RAbstractStringVector naStringsVec, boolean flush, boolean fill, RAbstractLogicalVector stripVec,
                      boolean quiet, boolean blSkip, boolean multiLine, int commentChar, boolean escapes,
                      String encoding, boolean skipNull) {
  
          LocalData data = new LocalData();
  
          // TODO: some sort of character translation happens here?
          data.sepchar = sep.isEmpty() ? 0 : sep.charAt(0);
  
          // TODO: some sort of character translation happens here?
          data.decchar = dec.charAt(0);
  
          // TODO: some sort of character translation happens here?
          data.quoteset = quotes.toCharArray();
  
          data.naStrings = naStringsVec;
  
          if (stripVec.getLength() != 1 && stripVec.getLength() != what.getLength()) {
              throw error(RError.Message.INVALID_LENGTH, "strip.white");
          }
          byte strip = stripVec.getDataAt(0);
  
          data.quiet = quiet;
  
          data.comchar = commentChar;
  
          data.escapes = escapes;
  
          if (encoding.equals("latin1")) {
              data.isLatin1 = true;
          }
          if (encoding.equals("UTF-8")) {
              data.isUTF8 = true;
          }
  
          data.skipNull = skipNull;
  
          // TODO: quite a few more things happen in GNU R around connections
          data.con = RConnection.fromIndex(file);
  
          data.save = 0;
  
          try (RConnection openConn = data.con.forceOpen("r")) {
              if (nskip > 0) {
                  openConn.readLines(nskip, true, skipNull);
              }
              if (what instanceof RList) {
                  return scanFrame((RList) what, nmax, nlines, flush, fill, strip == RRuntime.LOGICAL_TRUE, blSkip, multiLine, data);
              } else {
                  return scanVector(what, nmax, nlines, flush, strip == RRuntime.LOGICAL_TRUE, blSkip, data);
              }
          } catch (IOException x) {
              throw error(RError.Message.CANNOT_READ_CONNECTION);
          }
      }
  
      private static int skipWhitespace(String s, int start) {
          int pos = start;
          while (pos < s.length() && (s.charAt(pos) == ' ' || s.charAt(pos) == '\t')) {
              pos++;
          }
          return pos;
      }
  
      private static boolean isInSet(char ch, char[] quoteset) {
          for (int i = 0; i < quoteset.length; i++) {
              if (ch == quoteset[i]) {
                  return true;
              }
          }
          return false;
      }
  
      private static String[] getQuotedItems(LocalData data, String s) {
          ArrayList<String> items = new ArrayList<>();
  
          char sepchar = data.sepchar;
          char[] quoteset = data.quoteset;
          int length = s.length();
          int pos = 0;
          if (sepchar == 0) {
              pos = skipWhitespace(s, pos);
          }
          if (pos == length) {
              return new String[0];
          }
          StringBuilder str = new StringBuilder();
          do {
              char ch = s.charAt(pos);
              if (sepchar == 0 && (ch == ' ' || ch == '\t')) {
                  pos = skipWhitespace(s, pos);
                  if (pos == length) {
                      break;
                  }
                  items.add(str.toString());
                  str.setLength(0);
              } else if (sepchar != 0 && ch == sepchar) {
                  pos++;
                  items.add(str.toString());
                  str.setLength(0);
              } else if (str.length() == 0 && isInSet(ch, quoteset)) {
                  char quoteStart = ch;
                  pos++;
                  while (true) {
                      if (pos == length) {
                          throw RError.error(RError.SHOW_CALLER, Message.INCOMPLETE_FINAL_LINE, s);
                      }
                      ch = s.charAt(pos++);
                      if (ch == quoteStart) {
                          if (pos < length && s.charAt(pos) == quoteStart) {
                              str.append(quoteStart);
                              pos++;
                          } else {
                              break;
                          }
                      } else {
                          str.append(ch);
                      }
                  }
              } else {
                  str.append(ch);
                  pos++;
              }
          } while (pos < s.length());
          items.add(str.toString());
  
          return items.toArray(new String[items.size()]);
      }
  
      private static String[] getItems(LocalData data, boolean blSkip) throws IOException {
          while (true) {
              String[] str = data.con.readLines(1, true, false);
              if (str == null || str.length == 0) {
                  return null;
              } else {
                  String[] items = getQuotedItems(data, str[0]);
                  if (blSkip && items.length == 0) {
                      continue;
                  } else {
                      return items.length == 0 ? new String[]{""} : items;
                  }
              }
          }
      }
  
      private void fillEmpty(int from, int to, int records, RList list, LocalData data) {
          for (int i = from; i < to; i++) {
              RVector<?> vec = (RVector<?>) list.getDataAt(i);
              vec.updateDataAtAsObject(records, extractItem(vec, "", data), naCheck);
          }
      }
  
      private RVector<?> scanFrame(RList what, int maxRecords, int maxLines, boolean flush, boolean fill, @SuppressWarnings("unused") boolean stripWhite, boolean blSkip, boolean multiLine,
                      LocalData data) throws IOException {
  
          int nc = what.getLength();
          if (nc == 0) {
              throw error(RError.Message.EMPTY_WHAT);
          }
          int blockSize = maxRecords > 0 ? maxRecords : (maxLines > 0 ? maxLines : SCAN_BLOCKSIZE);
  
          RList list = RDataFactory.createList(nc);
          for (int i = 0; i < nc; i++) {
              if (what.getDataAt(i) == RNull.instance) {
                  throw error(RError.Message.INVALID_ARGUMENT, "what");
              } else {
                  RAbstractVector vec = castVector(what.getDataAt(i));
                  list.updateDataAt(i, vec.createEmptySameType(blockSize, RDataFactory.COMPLETE_VECTOR), null);
              }
          }
          list.setNames(getNames.getNames(what));
  
          naCheck.enable(true);
  
          return scanFrameInternal(maxRecords, maxLines, flush, fill, blSkip, multiLine, data, nc, blockSize, list);
      }
  
      @TruffleBoundary
      private RVector<?> scanFrameInternal(int maxRecords, int maxLines, boolean flush, boolean fill, boolean blSkip, boolean multiLine, LocalData data, int nc, int initialBlockSize, RList list)
                      throws IOException {
          int blockSize = initialBlockSize;
          int n = 0;
          int lines = 0;
          int records = 0;
          while (true) {
              // TODO: does not do any fancy stuff, like handling comments
              String[] strItems = getItems(data, blSkip);
              if (strItems == null) {
                  break;
              }
  
              boolean done = false;
              for (int i = 0; i < Math.max(nc, strItems.length); i++) {
  
                  if (n == strItems.length) {
                      if (fill) {
                          fillEmpty(n, nc, records, list, data);
                          records++;
                          n = 0;
                          break;
                      } else if (!multiLine) {
                          throw error(RError.Message.LINE_ELEMENTS, lines + 1, nc);
                      } else {
                          strItems = getItems(data, blSkip);
                          // Checkstyle: stop modified control variable check
                          i = 0;
                          // Checkstyle: resume modified control variable check
                          if (strItems == null) {
                              done = true;
                              break;
                          }
                      }
                  }
                  Object item = extractItem((RAbstractVector) list.getDataAt(n), strItems[i], data);
  
                  if (records == blockSize) {
                      // enlarge the vector
                      blockSize = blockSize * 2;
                      for (int j = 0; j < nc; j++) {
                          RVector<?> vec = (RVector<?>) list.getDataAt(j);
                          vec = vec.copyResized(blockSize, false);
                          list.updateDataAt(j, vec, null);
                      }
                  }
  
                  RVector<?> vec = (RVector<?>) list.getDataAt(n);
                  vec.updateDataAtAsObject(records, item, naCheck);
                  n++;
                  if (n == nc) {
                      records++;
                      n = 0;
                      if (records == maxRecords) {
                          done = true;
                          break;
                      }
                      if (flush) {
                          break;
                      }
                  }
              }
              if (done) {
                  break;
              }
              lines++;
              if (lines == maxLines) {
                  break;
              }
          }
  
          if (n > 0 && n < nc) {
              if (!fill) {
                  warning(RError.Message.ITEMS_NOT_MULTIPLE);
              }
  