/*
 * call-seq:
 *   IDN::Punycode.encode(string) => string
 *
 * Converts a string in UTF-8 format to Punycode.
 *
 * Raises IDN::Punycode::PunycodeError on failure.
 */

static VALUE encode(VALUE self, VALUE str)
{
  int rc;
  punycode_uint *ustr;
  size_t len;
  size_t buflen = 0x100;
  char *buf = NULL;
  VALUE retv;

  str = rb_check_convert_type(str, T_STRING, "String", "to_s");
  ustr = stringprep_utf8_to_ucs4(RSTRING(str)->ptr, RSTRING(str)->len, &len);

  while (1) {
    buf = realloc(buf, buflen);

    if (buf == NULL) {
      xfree(ustr);
      rb_raise(rb_eNoMemError, "cannot allocate memory (%d bytes)", buflen);
      return Qnil;
    }

    rc = punycode_encode(len, ustr, NULL, &buflen, buf);

    if (rc == PUNYCODE_SUCCESS) {
      break;
    } else if (rc == PUNYCODE_BIG_OUTPUT) {
      buflen += 0x100;
    } else {
      xfree(ustr);
      xfree(buf);
      rb_raise(ePunycodeError, "%s (%d)", punycode_strerror(rc), rc);
      return Qnil;
    }
  }

  retv = rb_str_new(buf, buflen);
  xfree(ustr);
  xfree(buf);
  return retv;
}