blob: 97771a5a1276a9f28bbfec7b649dc07e2d541c90 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef IMPALA_EXPERIMENTS_STRING_SEARCH_SSE_H
#define IMPALA_EXPERIMENTS_STRING_SEARCH_SSE_H
#include "common/compiler-util.h"
#include "common/logging.h"
#include "runtime/string-value.h"
namespace impala {
// This class implements strstr for non-null terminated strings. It wraps the
// standard strstr function (which is sse4 optimized).
/// NOTE: Because this modifies the strings in place, you *cannot* pass it data
/// that was from the data section (e.g. StringSearchSSE(StringValue("abcd", 4));
/// TODO: this is still 25% slower than just calling strstr. Look into why
/// TODO: this cannot be used with data containing nulls (I think)
class StringSearchSSE {
public:
/// Create a search needle for *null-terminated strings*. This is more
/// efficient for searching and should be used if either the needle already
/// happens to be null terminated or the needle will be reused repeatedly, in
/// which case the copy and null terminate is worth it.
/// The caller owns the memory for the needle.
StringSearchSSE(const char* needle) :
needle_str_val_(NULL), needle_cstr_(needle) {
needle_len_ = strlen(needle);
}
/// Create a search needle from a non-null terminated string. The caller
/// owns the memory for the needle.
StringSearchSSE(const StringValue* needle) :
needle_str_val_(needle), needle_cstr_(NULL) {
needle_len_ = needle->len;
}
StringSearchSSE() : needle_str_val_(NULL), needle_cstr_(NULL), needle_len_(0) {}
/// Search for needle in haystack.
/// Returns the offset into str if the needle exists
/// Returns -1 if the needle is not found
/// str will be temporarily modified for the duration of the function
int Search(const StringValue& haystack) const {
// Edge cases
if (UNLIKELY(haystack.len == 0 && needle_len_ == 0)) return 0;
if (UNLIKELY(haystack.len == 0)) return -1;
if (UNLIKELY(needle_len_ == 0)) return 0;
if (UNLIKELY(haystack.len < needle_len_)) return -1;
int result = -1;
// temporarily null terminated input string
char last_char_haystack = haystack.ptr[haystack.len - 1];
haystack.ptr[haystack.len - 1] = '\0';
// Use strchr if needle_len_ is 1
if (needle_len_ == 1) {
char c = (needle_str_val_ == NULL) ? needle_cstr_[0] : needle_str_val_->ptr[0];
char* s = strchr(haystack.ptr, c);
if (s != NULL) {
result = s - haystack.ptr;
} else if (last_char_haystack == c) {
result = haystack.len - 1;
}
// Undo change to haystack
haystack.ptr[haystack.len - 1] = last_char_haystack;
return result;
}
// needle is null terminated. We just need to run strstr on the
// null terminated haystack, and if there is no match, try a match
// on the last needle_len chars.
if (LIKELY(needle_cstr_ != NULL)) {
char* s = strstr(haystack.ptr, needle_cstr_);
// Undo change to haystack
haystack.ptr[haystack.len - 1] = last_char_haystack;
if (s != NULL) {
result = s - haystack.ptr;
} else {
// If we didn't find a match, try the last needle->len chars
char* end = haystack.ptr + haystack.len - needle_len_;
bool match = true;
for (int i = 0; i < needle_len_; ++i) {
if (LIKELY(end[i] != needle_cstr_[i])) {
match = false;
break;
}
}
if (UNLIKELY(match)) result = haystack.len - needle_len_;
}
return result;
} else {
// Needle is not null terminated. Terminate it on the fly.
const char last_char_needle = needle_str_val_->ptr[needle_len_ - 1];
needle_str_val_->ptr[needle_len_ - 1] = '\0';
int offset = 0;
char* haystack_pos = haystack.ptr;
while (offset <= haystack.len - needle_len_) {
char* search = strstr(haystack_pos, needle_str_val_->ptr);
// If the shortened strings didn't match, then the full strings
// must not match
if (search == NULL) break;
offset += (search - haystack_pos);
// The match happened at the very end of string. This is the case where:
// needle = "abc" (null terminated to "ab")
// haystack is "aaabc" (null terminated to "aaab")
// In this case, we just need to compare the last chars from both.
if (offset == haystack.len - needle_len_) {
if (last_char_needle == last_char_haystack) result = offset;
break;
} else {
// If the shortened strings match, match the last character
// Partial match within the string. Compare the last and if doesn't
// match, continue searching
if (search[needle_len_ - 1] == last_char_needle) {
result = offset;
break;
} else {
// Advance the haystack. This can be made more efficient by using
// a boyer-moore like approach (instead of just advancing by one).
haystack_pos = search + 1;
++offset;
}
}
}
// Undo change to haystack
haystack.ptr[haystack.len - 1] = last_char_haystack;
// Undo changes to needle
needle_str_val_->ptr[needle_len_ - 1] = last_char_needle;
return result;
}
}
private:
/// Only one of these two will be non-null. Both are unowned.
const StringValue* needle_str_val_;
const char* needle_cstr_;
int needle_len_;
};
}
#endif