<commit_before>#include "builtin/bytearray.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/integer.hpp"
#include "builtin/nativemethod.hpp"
#include "builtin/object.hpp"
#include "builtin/string.hpp"

#include "capi/capi.hpp"
#include "capi/include/ruby.h"

#include <string.h>

using namespace rubinius;
using namespace rubinius::capi;

namespace rubinius {
  namespace capi {

    String* capi_get_string(NativeMethodEnvironment* env, VALUE string) {
      if(!env) env = NativeMethodEnvironment::get();

      Handle* handle = Handle::from(string);
      String* str = c_as<String>(handle->object());
      handle->flush(env);

      return str;
    }

    void capi_update_string(NativeMethodEnvironment* env, VALUE string) {
      if(!env) env = NativeMethodEnvironment::get();

      Handle* handle = Handle::from(string);
      handle->update(env);
    }

    /* We use the garbage collector's feature to "pin" an Object at a
     * particular memory location to allow C code to write directly into the
     * contets of a Ruby String (actually, a ByteArray, which provides the
     * storage for String). Since any method on String that mutates self may
     * cause a new ByteArray to be created, we always check whether the
     * String is pinned and update the RString structure unconditionally.
     */
    void ensure_pinned(NativeMethodEnvironment* env, String* string, RString* rstring) {
      ByteArray* ba = string->data();
      size_t byte_size = ba->size();

      if(!ba->pinned_p()) {
        ba = ByteArray::create_pinned(env->state(), byte_size);
        memcpy(ba->raw_bytes(), string->byte_address(), byte_size);
        string->data(env->state(), ba);
      }

      char* ptr = reinterpret_cast<char*>(ba->raw_bytes());

      ptr[byte_size-1] = 0;
      rstring->dmwmb = rstring->ptr = ptr;
      rstring->len = string->size();
      rstring->aux.capa = byte_size;
      rstring->aux.shared = Qfalse;
    }

    /* We are in C-land returning to Ruby-land. The value of RString.len
     * may have changed. We raise an exception if the value of len exceeds
     * the capacity of the underlying ByteArray or if the value of ptr has
     * changed.
     */
    void flush_cached_rstring(NativeMethodEnvironment* env, Handle* handle) {
      if(handle->is_rstring()) {
        String* string = c_as<String>(handle->object());
        RString* rstring = handle->get_rstring();

        if(rstring->ptr != rstring->dmwmb) {
          rb_raise(rb_eRuntimeError,
              "changing the value of RSTRING(obj)->ptr is not supported");
        }

        if(rstring->len > string->data()->size()) {
          rb_raise(rb_eRuntimeError,
              "RSTRING(obj)->len must be <= capacity of the String");
        } else if(rstring->len != string->num_bytes()->to_native()) {
          // TODO: encoding support will need to define whether ->len
          // means bytes or characters.
          string->num_bytes(env->state(), Fixnum::from(rstring->len));
          string->characters(env->state(), Fixnum::from(rstring->len));
          string->hash_value(env->state(), reinterpret_cast<Fixnum*>(RBX_Qnil));
        }
      }
    }

    /* We were in Ruby-land and we are heading into C-land. In Ruby-land, we
     * may have updated the existing String bytes, appended more, shifted the
     * start of the String, or replaced the ByteArray so we ensure that the
     * RString structure contents are update.
     */
    void update_cached_rstring(NativeMethodEnvironment* env, Handle* handle) {
      if(handle->is_rstring()) {
        String* string = c_as<String>(handle->object());
        RString* rstring = handle->get_rstring();

        ensure_pinned(env, string, rstring);
      }
    }

    RString* Handle::as_rstring(NativeMethodEnvironment* env, int cache_level) {
      String* string = c_as<String>(object());

      if(type_ != cRString) {
        type_ = cRString;

        string->unshare(env->state());

        RString* str = new RString;
        as_.rstring = str;

        if(cache_level == RSTRING_CACHE_UNSAFE) {
          flush_ = flush_cached_rstring;
          update_ = update_cached_rstring;
          env->check_tracked_handle(this, true);
        } else {
          env->check_tracked_handle(this, false);
        }
      }

      // The underlying String may have changed since we last got the
      // associated RString structure.
      ensure_pinned(env, string, as_.rstring);

      return as_.rstring;<commit_msg>Bound the C-API cache for a String when first creating it. Closes #702.<commit_after>#include "builtin/bytearray.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/integer.hpp"
#include "builtin/nativemethod.hpp"
#include "builtin/object.hpp"
#include "builtin/string.hpp"

#include "capi/capi.hpp"
#include "capi/include/ruby.h"

#include <string.h>

using namespace rubinius;
using namespace rubinius::capi;

namespace rubinius {
  namespace capi {

    String* capi_get_string(NativeMethodEnvironment* env, VALUE string) {
      if(!env) env = NativeMethodEnvironment::get();

      Handle* handle = Handle::from(string);
      String* str = c_as<String>(handle->object());
      handle->flush(env);

      return str;
    }

    void capi_update_string(NativeMethodEnvironment* env, VALUE string) {
      if(!env) env = NativeMethodEnvironment::get();

      Handle* handle = Handle::from(string);
      handle->update(env);
    }

    /* We use the garbage collector's feature to "pin" an Object at a
     * particular memory location to allow C code to write directly into the
     * contets of a Ruby String (actually, a ByteArray, which provides the
     * storage for String). Since any method on String that mutates self may
     * cause a new ByteArray to be created, we always check whether the
     * String is pinned and update the RString structure unconditionally.
     */
    void ensure_pinned(NativeMethodEnvironment* env, String* string, RString* rstring) {
      ByteArray* ba = string->data();
      size_t byte_size = ba->size();

      if(!ba->pinned_p()) {
        ba = ByteArray::create_pinned(env->state(), byte_size);
        memcpy(ba->raw_bytes(), string->byte_address(), byte_size);
        string->data(env->state(), ba);
      }

      char* ptr = reinterpret_cast<char*>(ba->raw_bytes());

      ptr[byte_size-1] = 0;
      rstring->dmwmb = rstring->ptr = ptr;
      rstring->len = string->size();
      rstring->aux.capa = byte_size;
      rstring->aux.shared = Qfalse;
    }

    /* We are in C-land returning to Ruby-land. The value of RString.len
     * may have changed. We raise an exception if the value of len exceeds
     * the capacity of the underlying ByteArray or if the value of ptr has
     * changed.
     */
    void flush_cached_rstring(NativeMethodEnvironment* env, Handle* handle) {
      if(handle->is_rstring()) {
        String* string = c_as<String>(handle->object());
        RString* rstring = handle->get_rstring();

        if(rstring->ptr != rstring->dmwmb) {
          rb_raise(rb_eRuntimeError,
              "changing the value of RSTRING(obj)->ptr is not supported");
        }

        if(rstring->len > string->data()->size()) {
          rb_raise(rb_eRuntimeError,
              "RSTRING(obj)->len must be <= capacity of the String");
        } else if(rstring->len != string->num_bytes()->to_native()) {
          // TODO: encoding support will need to define whether ->len
          // means bytes or characters.
          string->num_bytes(env->state(), Fixnum::from(rstring->len));
          string->characters(env->state(), Fixnum::from(rstring->len));
          string->hash_value(env->state(), reinterpret_cast<Fixnum*>(RBX_Qnil));
        }
      }
    }

    /* We were in Ruby-land and we are heading into C-land. In Ruby-land, we
     * may have updated the existing String bytes, appended more, shifted the
     * start of the String, or replaced the ByteArray so we ensure that the
     * RString structure contents are update.
     */
    void update_cached_rstring(NativeMethodEnvironment* env, Handle* handle) {
      if(handle->is_rstring()) {
        String* string = c_as<String>(handle->object());
        RString* rstring = handle->get_rstring();

        ensure_pinned(env, string, rstring);
      }
    }

    RString* Handle::as_rstring(NativeMethodEnvironment* env, int cache_level) {
      String* string = c_as<String>(object());
      bool unset = type_ != cRString;

      if(unset) {
        type_ = cRString;

        string->unshare(env->state());

        RString* str = new RString;
        as_.rstring = str;

        if(cache_level == RSTRING_CACHE_UNSAFE) {
          flush_ = flush_cached_rstring;
          update_ = update_cached_rstring;
          env->check_tracked_handle(this, true);
        } else {
          env->check_tracked_handle(this, false);
        }
      }

      // The underlying String may have changed since we last got the
      // associated RString structure.
      ensure_pinned(env, string, as_.rstring);

      /* In Ruby, regardless of the contents in the ByteArray for a String,
       * the String's size is the authority on the length of the String.
       * However, for the C-API, we cannot assume this because C code may be
       * writing to the cache behind our backs, so we faithfully keep the full
       * contents of the ByteArray and the C cache in sync. If there has never
       * been a cache created for a string, however, we must set put a null
       * byte in the cache based on the String size.
       */
      if(unset) as_.rstring->ptr[string->size()] = 0;

      return as_.rstring;
{'fraction_non_alphanumeric': 0.11682242990654206, 'fraction_numerical': 0.0008307372793354102, 'mean_word_length': 2.806719367588933, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 115, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4509891', 'n_tokens_mistral': 2839, 'n_tokens_neox': 2787, 'n_words': 825}