| 231 | |
| 232 | template<bool DELIMITED_TUPLES> |
| 233 | int64_t DelimitedTextParser<DELIMITED_TUPLES>::FindFirstInstance(const char* buffer, |
| 234 | int64_t len) { |
| 235 | int64_t tuple_start = 0; |
| 236 | const char* buffer_start = buffer; |
| 237 | bool found = false; |
| 238 | |
| 239 | DCHECK(DELIMITED_TUPLES); |
| 240 | // If the last char in the previous buffer was \r then either return the start of |
| 241 | // this buffer or skip a \n at the beginning of the buffer. |
| 242 | if (last_row_delim_offset_ != -1) { |
| 243 | if (*buffer_start == '\n') return 1; |
| 244 | return 0; |
| 245 | } |
| 246 | restart: |
| 247 | found = false; |
| 248 | |
| 249 | #ifdef __x86_64__ |
| 250 | if (CpuInfo::IsSupported(CpuInfo::SSE4_2)) { |
| 251 | __m128i xmm_buffer, xmm_tuple_mask; |
| 252 | while (len - tuple_start >= SSEUtil::CHARS_PER_128_BIT_REGISTER) { |
| 253 | // TODO: can we parallelize this as well? Are there multiple sse execution units? |
| 254 | // Load the next 16 bytes into the xmm register and do strchr for the |
| 255 | // tuple delimiter. |
| 256 | xmm_buffer = _mm_loadu_si128(reinterpret_cast<const __m128i*>(buffer)); |
| 257 | xmm_tuple_mask = SSE4_cmpestrm<SSEUtil::STRCHR_MODE>(xmm_tuple_search_, |
| 258 | num_tuple_delims_, xmm_buffer, SSEUtil::CHARS_PER_128_BIT_REGISTER); |
| 259 | int tuple_mask = _mm_extract_epi16(xmm_tuple_mask, 0); |
| 260 | if (tuple_mask != 0) { |
| 261 | found = true; |
| 262 | // Find first set bit (1-based) |
| 263 | int i = ffs(tuple_mask); |
| 264 | tuple_start += i; |
| 265 | buffer += i; |
| 266 | break; |
| 267 | } |
| 268 | tuple_start += SSEUtil::CHARS_PER_128_BIT_REGISTER; |
| 269 | buffer += SSEUtil::CHARS_PER_128_BIT_REGISTER; |
| 270 | } |
| 271 | } |
| 272 | #endif |
| 273 | if (!found) { |
| 274 | for (; tuple_start < len; ++tuple_start) { |
| 275 | char c = *buffer++; |
| 276 | if (c == tuple_delim_ || (c == '\r' && tuple_delim_ == '\n')) { |
| 277 | ++tuple_start; |
| 278 | found = true; |
| 279 | break; |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | if (!found) return -1; |
| 285 | |
| 286 | if (process_escapes_) { |
| 287 | // Scan backwards for escape characters. We do this after |
| 288 | // finding the tuple break rather than during the (above) |
| 289 | // forward scan to make the forward scan faster. This will |
| 290 | // perform worse if there are many characters right before the |
no outgoing calls