# Latest changes from mainline fast\_xs

**URL:** <https://www.ruby-forum.com/t/latest-changes-from-mainline-fast-xs/174225>\
**Category:** Ruby\
**Created:** [August 22, 2009, 10:25pm UTC](https://www.ruby-forum.com/t/latest-changes-from-mainline-fast-xs/174225 "2009-08-22T22:25:55Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![ericw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/97f17d/32.png) [@ericw](https://www.ruby-forum.com/u/ericw)\
**Post date:** [August 22, 2009, 10:25pm UTC](https://www.ruby-forum.com/t/latest-changes-from-mainline-fast-xs/174225/1 "2009-08-22T22:25:55Z")

</div>

Hi all,

Hopefully the new maintainer of Hpricot will see this because I have no  
idea who to send it to now…

I originally had a version of this patch posted in \_why’s Trac sometime  
in 2008 until he took Trac down. I’ve tried emailing him directly on  
several occasions since and never got a response, last was February  
2009. Come to think of it, the only time I ever got any response from  
him was my initial email to him about fast\_xs back in October 2007.

So here it is, I’ve rebased my patch against  
2c961095954d5aaa5c046f4c773c62c3d5902ef4 on  
git://github.com/whymirror/hpricot.git

Also I have an entire repo up on git://git.bogomips.org/hpricot and  
viewable from [http://git.bogomips.org/cgit/hpricot.git](http://git.bogomips.org/cgit/hpricot.git)

From 636e3f453b2bbc0c7486b91eda452fe4767e4bbc Mon Sep 17 00:00:00 2001  
From: Eric W. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
Date: Sat, 7 Feb 2009 21:31:15 -0800  
Subject: [PATCH] \* ext/fast\_xs: latest changes from mainline fast\_xs

- alloca() dependency removed. An extra memory allocation and  
memory copy is saved by writing directly to the string object  
returned by rb\_str\_new(0, len) (both the Ruby 1.8.6 and 1.9  
code has this usage in it, so it should be safe 🙂  
This also allows fast\_xs to work on strings larger than  
the stack size.
- general readability and cleanups
- fast\_xs works with Ruby 1.9
- Don’t rely on ruby\_digitmap being a valid symbol

* * *

ext/fast\_xs/fast\_xs.c | 101  
+++++++++++++++++++++++±------------------------  
1 files changed, 50 insertions(+), 51 deletions(-)

## diff --git a/ext/fast\_xs/fast\_xs.c b/ext/fast\_xs/fast\_xs.c index 4a30a6c…04b175f 100644 — a/ext/fast\_xs/fast\_xs.c +++ b/ext/fast\_xs/fast\_xs.c @@ -1,8 +1,5 @@ -#define VERSION “0.1”

#include \<ruby.h\>  
#include \<assert.h\>  
-/\* #include \<stdio.h\> \*/

#ifndef RARRAY\_LEN  
#define RARRAY\_LEN(arr) RARRAY(arr)-\>len  
@@ -72,11 +69,6 @@ static const int cp\_1252[] = {  
n = cp\_1252[n - 128];   
} while(0)

-#define return\_const\_len(x) do { \

- memcpy(buf, x, sizeof(x) - 1); \
- return (sizeof(x) - 1);   
-} while (0)
- 

static inline size\_t bytes\_for(int n)  
{  
if (n \< 1000)  
@@ -91,18 +83,24 @@ static inline size\_t bytes\_for(int n)  
return sizeof(“�”) - 1;  
}

-static long escape(char \*buf, int n)  
+static size\_t escape(char \*buf, int n)  
{  
+  
+#define return\_const\_len(x) do { \

- memcpy(buf, x, sizeof(x) - 1); \
- return (sizeof(x) - 1);   
+} while (0)
- /\* handle ASCII first \*/  
if (likely(n \< 128)) {

- if (likely(n \>= 0x20 || n == 0x9 || n == 0xA || n == 0xD)) {
- 

```
 if (unlikely(n == 34))

```

- if (likely(n \>= 0x20 || n == ‘\t’ || n == ‘\n’ || n == ‘\r’)) {
- 

```
 if (unlikely(n == '"'))
   return_const_len("&quot;");

```

- 

```
 if (unlikely(n == 38))

```

- 

```
 if (unlikely(n == '&'))
   return_const_len("&amp;");

```

- 

```
 if (unlikely(n == 60))

```

- 

```
 if (unlikely(n == '<'))
   return_const_len("&lt;");

```

- 

```
 if (unlikely(n == 62))

```

- 

```
 if (unlikely(n == '>'))
   return_const_len("&gt;");
 buf[0] = (char)n;
 return 1;

```

@@ -112,16 +110,18 @@ static long escape(char \*buf, int n)  
return 1;  
}

+#undef return\_const\_len  
+  
CP\_1252\_ESCAPE(n);

if (VALID\_VALUE(n)) {  
/\* return snprintf(buf, sizeof(“�”), “&#%i;”, n); \*/

- RUBY\_EXTERN const char ruby\_digitmap[];
- int rv = 3; /\* &#; \*/

- static const char digitmap[] = “0123456789”;
- size\_t rv = sizeof(“&#;”) - 1;  
buf += bytes\_for(n);  
\*–buf = ‘;’;  
do {

- 

```
 *--buf = ruby_digitmap[(int)(n % 10)];

```

- 

```
 *--buf = digitmap[(int)(n % 10)];
 ++rv;

```

} while (n /= 10);  
\*–buf = ‘#’;  
@@ -132,27 +132,6 @@ static long escape(char \*buf, int n)  
return 1;  
}

## -#undef return\_const\_len

-static long escaped\_len(int n)  
-{

- if (likely(n \< 128)) {
- if (unlikely(n == 34))
- 

```
 return (sizeof("&quot;") - 1);

```

- if (unlikely(n == 38))
- 

```
 return (sizeof("&amp;") - 1);

```

- if (unlikely(n == 60 || n == 62))
- 

```
 return (sizeof("&gt;") - 1);

```

- return 1;
- }
- 
- CP\_1252\_ESCAPE(n);
- 
- if (VALID\_VALUE(n))
- return bytes\_for(n);
- return 1;  
-}
- 

static VALUE unpack\_utf8(VALUE self)  
{  
return rb\_funcall(self, unpack\_id, 1, U\_fmt);  
@@ -163,28 +142,48 @@ static VALUE unpack\_uchar(VALUE self)  
return rb\_funcall(self, unpack\_id, 1, C\_fmt);  
}

-VALUE fast\_xs(VALUE self)  
+/\*

- 
  - escapes strings for XML

- 
  - The double-quote (") character is translated to “"”

- \*/  
+static VALUE fast\_xs(VALUE self)  
{  
long i;

- struct RArray \*array;
- char \*s, \*c;
- long s\_len = 0;

- VALUE array;
- char \*c;
- size\_t s\_len;  
VALUE \*tmp;
- VALUE rv;
- 
- array = rb\_rescue(unpack\_utf8, self, unpack\_uchar, self);
- 
- for (tmp = RARRAY\_PTR(array), s\_len = i = RARRAY\_LEN(array);
- 

```
  --i >= 0;

```

- 

```
  tmp++) {

```

- int n = NUM2INT(\*tmp);
- if (likely(n \< 128)) {
- 

```
 if (unlikely(n == '"'))

```

- 

```
   s_len += (sizeof("&quot;") - 2);

```

- 

```
 if (unlikely(n == '&'))

```

- 

```
   s_len += (sizeof("&amp;") - 2);

```

- 

```
 if (unlikely(n == '>' || n == '<'))

```

- 

```
   s_len += (sizeof("&gt;") - 2);

```

- 

```
 continue;

```

- }

- array = RARRAY(rb\_rescue(unpack\_utf8, self, unpack\_uchar, self));

- CP\_1252\_ESCAPE(n);

- tmp = RARRAY\_PTR(array);
- for (i = RARRAY\_LEN(array); --i \>= 0; tmp++)
- s\_len += escaped\_len(NUM2INT(\*tmp));

- if (VALID\_VALUE(n))
- 

```
 s_len += bytes_for(n) - 1;

```

- }

- c = s = alloca(s\_len + 1);

- rv = rb\_str\_new(NULL, s\_len);
- c = RSTRING\_PTR(rv);

- tmp = RARRAY\_PTR(array);
- for (i = RARRAY\_LEN(array); --i \>= 0; tmp++)

- for (tmp = RARRAY\_PTR(array), i = RARRAY\_LEN(array); --i \>= 0; tmp++)  
c += escape(c, NUM2INT(\*tmp));

- \*c = ‘\0’;
- return rb\_str\_new(s, s\_len);

- return rv;  
}

void Init\_fast\_xs(void)
