Skip to content

Commit

Permalink
move Date's s3e internal into native (based on MRI's C impl)
Browse files Browse the repository at this point in the history
  • Loading branch information
kares committed Jul 19, 2018
1 parent 5c280fa commit 9892b3c
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 54 deletions.
134 changes: 134 additions & 0 deletions core/src/main/java/org/jruby/ext/date/RubyDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -1655,6 +1655,140 @@ public static RubyInteger _comp_year69(ThreadContext context, IRubyObject self,
return y;
}

@JRubyMethod(name = "s3e", meta = true, required = 4, optional = 1, visibility = Visibility.PRIVATE)
public static IRubyObject _s3e(ThreadContext context, IRubyObject self, IRubyObject[] args) {
final IRubyObject nil = context.nil;

final RubyHash hash = (RubyHash) args[0];
RubyString y = args[1] == nil ? null : (RubyString) args[1];
RubyString m = args[2] == nil ? null : (RubyString) args[2];
RubyString d = args[3] == nil ? null : (RubyString) args[3];
boolean bc = args.length > 4 ? args[4].isTrue() : false;

Boolean comp = null; RubyString oy, om, od;

if (d == null && y != null && m != null) {
oy = y; om = m; od = d;
y = od; m = oy; d = om;
}

if (y == null) {
if (d != null && d.strLength() > 2) {
y = d; d = null;
} else if (d != null && strPtr(d, '\'')) {
y = d; d = null;
}
}


if (y != null) {
int s = skipNonDigitsAndSign(y);
int bp = s;
char c = s < y.strLength() ? y.charAt(s) : '\0';
if (c == '+' || c == '-') s++;
int ep = skipDigits(y, s);
if (ep != y.strLength()) {
oy = y; y = d;
d = (RubyString) oy.substr19(context.runtime, bp, ep - bp);
}
}

if (m != null) {
if (strPtr(m, '\'') || m.strLength() > 2) {
/* us -> be */
oy = y; om = m; od = d;
y = om; m = od; d = oy;
}
}

if (d != null) {
if (strPtr(d, '\'') || d.strLength() > 2) {
oy = y; od = d;
y = od; d = oy;
}
}

if (y != null) {
boolean sign = false;

int s = skipNonDigitsAndSign(y);

int bp = s;
char c = s < y.strLength() ? y.charAt(s) : '\0';
if (c == '+' || c == '-') {
s++; sign = true;
}
if (sign) comp = false;
int ep = skipDigits(y, s);
if (ep - s > 2) comp = false;

RubyInteger iy = cstr2num(context.runtime, y, bp, ep);
if (bc) iy = (RubyInteger) iy.negate().op_plus(context, 1);
set_hash(context, hash, "year", iy);
}

//if (bc) set_hash("_bc", Qtrue);

if (m != null) {
int s = skipNonDigitsAndSign(m);

int bp = s;
int ep = skipDigits(m, s);
set_hash(context, hash, "mon", cstr2num(context.runtime, m, bp, ep));
}

if (d != null) {
int s = skipNonDigitsAndSign(d);

int bp = s;
int ep = skipDigits(d, s);
set_hash(context, hash, "mday", cstr2num(context.runtime, d, bp, ep));
}

if (comp != null) set_hash(context, hash, "_comp", context.runtime.newBoolean(comp));

return hash;
}

private static void set_hash(final ThreadContext context, RubyHash hash, String key, IRubyObject val) {
hash.fastASet(context.runtime.newSymbol(key), val);
}

private static RubyInteger cstr2num(Ruby runtime, RubyString str, int bp, int ep) {
if (bp == ep) return RubyFixnum.zero(runtime);
return ConvertBytes.byteListToInum(runtime, str.getByteList(), bp, ep, 10, true);
}

private static boolean strPtr(RubyString str, char c) {
return str.strLength() > 0 && str.charAt(0) == c;
}

private static boolean isDigit(char c) {
switch (c) {
case '0': case '1': case '2': case '3': case '4': return true;
case '5': case '6': case '7': case '8': case '9': return true;
default: return false;
}
}

private static int skipNonDigitsAndSign(RubyString str) {
int s = 0;
while (s < str.length()) {
char c = str.charAt(s);
if (isDigit(c) || (c == '+' || c == '-')) break;
s++;
}
return s;
}

private static int skipDigits(RubyString str, int off) {
int i = off;
for (; i < str.length(); i++) {
if (!isDigit(str.charAt(i))) return i;
}
return i;
}

// Java API

/**
Expand Down
14 changes: 14 additions & 0 deletions core/src/main/java/org/jruby/util/ConvertBytes.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ public ConvertBytes(Ruby runtime, ByteList str, int base, boolean badcheck) {
this.base = base;
}

ConvertBytes(Ruby runtime, ByteList str, int off, int end, int base, boolean badcheck) {
this.runtime = runtime;
this.str = str;
this.beg = off + str.getBegin();
this.data = str.getUnsafeBytes();
this.end = str.getBegin() + end;
this.badcheck = badcheck;
this.base = base;
}

@Deprecated
public ConvertBytes(Ruby runtime, ByteList str, int base, boolean badcheck, boolean is19) {
this(runtime, str, base, badcheck);
Expand Down Expand Up @@ -233,6 +243,10 @@ public static RubyInteger byteListToInum(Ruby runtime, ByteList str, int base, b
return new ConvertBytes(runtime, str, base, badcheck).byteListToInum();
}

public static RubyInteger byteListToInum(Ruby runtime, ByteList str, int off, int end, int base, boolean badcheck) {
return new ConvertBytes(runtime, str, off, end, base, badcheck).byteListToInum();
}

@Deprecated
public static RubyInteger byteListToInum19(Ruby runtime, ByteList str, int base, boolean badcheck) {
return byteListToInum(runtime, str, base, badcheck);
Expand Down
56 changes: 3 additions & 53 deletions lib/ruby/stdlib/date/format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,56 +141,6 @@ def jisx0301
end
end

def self.s3e(e, y, m, d, bc=false)
y, m, d = d, y, m if y && m && !d

if y == nil
if d && d.size > 2
y = d
d = nil
end
if d && d[0].eql?("'")
y = d
d = nil
end
end

if y
s = y.scan(/(\d+)(.+)?/)
if s[0] && s[0][1] # $2
y, d = d, s[0][0] # $1
end
end

if m
if m[0].eql?("'") || m.size > 2
y, m, d = m, d, y # us -> be
end
end

if d
if d[0].eql?("'") || d.size > 2
y, d = d, y
end
end

if y
if match = y.match(/([-+])?(\d+)/)
c = false if match[1] || match[2].size > 2
end
y = match&.[](0).to_i
e[:year] = bc ? -y + 1 : y
end

e[:mon] = m.match(/\d+/)&.[](0).to_i if m

e[:mday] = d.match(/\d+/)&.[](0).to_i if d

e[:_comp] = c if c != nil

end
private_class_method :s3e

ABBR_MONTHS_KEYS = Format::ABBR_MONTHS.keys.join('|').freeze
private_constant :ABBR_MONTHS_KEYS

Expand Down Expand Up @@ -837,9 +787,9 @@ def self._jisx0301(str) # :nodoc:
end
end

def self.set_zone(h, zone) # :nodoc:
h[:zone] = zone
h[:offset] = zone_to_diff(zone)
def self.set_zone(hash, zone) # :nodoc:
hash[:zone] = zone
hash[:offset] = zone_to_diff(zone)
end
private_class_method :set_zone

Expand Down
1 change: 0 additions & 1 deletion test/mri/excludes/TestDateParse.rb
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
exclude :test__parse_odd_offset, ''

0 comments on commit 9892b3c

Please sign in to comment.