The funky bit computations should be better documented IMO. "it works" is funny but does _not_ inspire confidence.
Gary ---------- Forwarded message ---------- From: <[email protected]> Date: Mon, Jan 12, 2015 at 3:53 AM Subject: [06/18] logging-log4j2 git commit: initial version To: [email protected] initial version Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/258fede7 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/258fede7 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/258fede7 Branch: refs/heads/LOG4J2-930 Commit: 258fede78aa9bbbbe5042c9af3152e59b7241834 Parents: 84bd3f9 Author: rpopma <[email protected]> Authored: Mon Jan 12 17:31:05 2015 +0900 Committer: rpopma <[email protected]> Committed: Mon Jan 12 17:31:05 2015 +0900 ---------------------------------------------------------------------- .../log4j/core/pattern/FastTimeFormatter.java | 209 +++++++++++++++++++ 1 file changed, 209 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/258fede7/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FastTimeFormatter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FastTimeFormatter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FastTimeFormatter.java new file mode 100644 index 0000000..3cc2081 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FastTimeFormatter.java @@ -0,0 +1,209 @@ +/* + * 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. + */ + +package org.apache.logging.log4j.core.pattern; + +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.logging.log4j.core.util.Assert; + +/** + * TODO doc + */ +public class FastTimeFormatter implements DatePatternConverter.Formatter { + + // cached value is non-volatile on purpose + private DatePart cached; + private final AtomicReference<DatePart> safe = new AtomicReference<DatePart>(); + private final String pattern; + private final Charset charset; + + private static class DatePart { + final long midnightToday; + final long midnightTomorrow; + final String dateTodayString; + final byte[] dateTodayBytes; + + public DatePart(final String pattern, final Charset charset) { + final Calendar cal = midnightToday(); + final Date today = cal.getTime(); + midnightToday = cal.getTimeInMillis(); + + cal.add(Calendar.DATE, 1); + midnightTomorrow = cal.getTimeInMillis(); + + final String datePart = extractDatePart(pattern); + if (datePart != null) { + dateTodayString = new SimpleDateFormat(datePart).format(today); + dateTodayBytes = dateTodayString.getBytes(charset); + } else { + dateTodayString = null; + dateTodayBytes = null; + } + } + + /** + * Returns the date part of a date+time format string, or {@code null} if the specified string does not contain + * a date part. + */ + private static String extractDatePart(final String pattern) { + final int offset = pattern.indexOf(DatePatternConverter.ABSOLUTE_TIME_PATTERN); + if (offset <= 0) { + return null; + } + return pattern.substring(0, offset); + } + + private static Calendar midnightToday() { + final Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal; + } + + public long millisSinceMidnight(final long now) { + return now - midnightToday; + } + } + + public FastTimeFormatter(final String pattern, final Charset charset) { + this.pattern = Assert.requireNonNull(pattern, "pattern is null"); + this.charset = Assert.requireNonNull(charset, "charset is null"); + cached = new DatePart(pattern, charset); + safe.set(cached); + } + + private DatePart datePart(final long now) { + DatePart result = cached; // postpone reading from volatile field + if (now >= result.midnightTomorrow) { + result = safe.get(); + if (now >= result.midnightTomorrow) { + result = new DatePart(pattern, charset); + cached = result; + safe.set(result); // store barrier; ensures cached field update is also visible to other threads + } + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.pattern.DatePatternConverter.Formatter#format(long, + * org.apache.logging.log4j.core.pattern.BinaryBuffer, java.nio.charset.Charset) + */ + public void format(final long time, final BinaryBuffer buffer, final Charset charset) { + format(time, buffer); + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.pattern.DatePatternConverter.Formatter#format(java.util.Date, + * org.apache.logging.log4j.core.pattern.Buffer) + */ + @Override + public void format(Date time, Buffer buffer) { + format(time.getTime(), buffer); + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.pattern.DatePatternConverter.Formatter#format(long, + * org.apache.logging.log4j.core.pattern.Buffer) + */ + @Override + public void format(long time, Buffer buffer) { + // Calculate values by getting the ms values first and do then + // shave off the hour minute and second values with multiplications + // and bit shifts instead of simple but expensive divisions. + + // Get daytime in ms which does fit into an int + // int ms = (int) (time % 86400000); + DatePart stamp = datePart(time); + int ms = (int) stamp.millisSinceMidnight(time); + + if (stamp.dateTodayBytes != null) { + buffer.append(stamp.dateTodayBytes); + } + + // well ... it works + final int hour = (int) (((ms >> 7) * 9773437L) >> 38); // hours = ms / 3600000; + ms -= 3600000 * hour; + + final int minute = (int) (((ms >> 5) * 2290650L) >> 32); // minutes = ms / 60000; + ms -= 60000 * minute; + + final int second = ((ms >> 3) * 67109) >> 23; // seconds = ms / 1000; + ms -= 1000 * second; + + // Hour + // 13/128 is nearly the same as /10 for values up to 65 + int temp = (hour * 13) >> 7; + buffer.append((byte) (temp + '0')); + + // Do subtract to get remainder instead of doing % 10 + buffer.append((byte) (hour - 10 * temp + '0')); + buffer.append((byte) ':'); + + // Minute + // 13/128 is nearly the same as /10 for values up to 65 + temp = (minute * 13) >> 7; + buffer.append((byte) (temp + '0')); + + // Do subtract to get remainder instead of doing % 10 + buffer.append((byte) (minute - 10 * temp + '0')); + buffer.append((byte) ':'); + + // Second + // 13/128 is nearly the same as /10 for values up to 65 + temp = (second * 13) >> 7; + buffer.append((byte) (temp + '0')); + buffer.append((byte) (second - 10 * temp + '0')); + buffer.append((byte) ','); + + // Millisecond + // 41/4096 is nearly the same as /100 + temp = (ms * 41) >> 12; + buffer.append((byte) (temp + '0')); + + ms -= 100 * temp; + temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10 + buffer.append((byte) (temp + '0')); + + ms -= 10 * temp; + buffer.append((byte) (ms + '0')); + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.pattern.DatePatternConverter.Formatter#toPattern() + */ + @Override + public String toPattern() { + return pattern; + } + +} -- E-Mail: [email protected] | [email protected] Java Persistence with Hibernate, Second Edition <http://www.manning.com/bauer3/> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> Spring Batch in Action <http://www.manning.com/templier/> Blog: http://garygregory.wordpress.com Home: http://garygregory.com/ Tweet! http://twitter.com/GaryGregory
