All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.rt.coverage.util.CoverageIOUtil Maven / Gradle / Ivy

/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed 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 com.intellij.rt.coverage.util;

import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author anna
 * @since 18-May-2009
 */
public class CoverageIOUtil {
  public static final int GIGA = 1000 * 1000 * 1000;
  private static final int STRING_HEADER_SIZE = 1;
  private static final int STRING_LENGTH_THRESHOLD = 255;

  private static final String LONGER_THAN_64K_MARKER = "LONGER_THAN_64K";


  private CoverageIOUtil() {
  }

  public final static class FileLock {
    final File myLock;

    private FileLock(final File target) {
      myLock = new File(target.getParentFile(), target.getName() + ".lck");
      if (myLock.getParentFile() != null) {
        myLock.getParentFile().mkdirs();
      }
    }

    private boolean isLocked() {
      return myLock.exists();
    }

    private boolean tryLock() {
      try {
        return myLock.createNewFile();
      } catch (IOException e) {
        return false;
      }
    }

    private boolean tryUnlock() {
      return myLock.delete();
    }

    public static FileLock lock(final File targetFile) {
      return lock(targetFile, 2 * 60 * 1000, 100);
    }

    /**
     * @param targetFile File to lock
     * @return Lock object
     */
    public static FileLock lock(final File targetFile, final long totalTimeoutMS, final long waitTimeMS) {
      final FileLock lock = new FileLock(targetFile);
      if (lock.myLock.exists()) {
        // If file exists, and it was created long time ago then looks like it stayed from a previous run.
        // This may lead to a race, as modification check and deletion is not atomic.
        final long current = System.currentTimeMillis();
        final long modified = lock.myLock.lastModified();
        if (modified != 0 && modified + totalTimeoutMS < current) {
          lock.tryUnlock();
        }
      }
      for (long timePassed = 0; timePassed < totalTimeoutMS; timePassed += waitTimeMS) {
        if (lock.tryLock()) return lock;
        wait(lock, waitTimeMS, "lock");
      }
      ErrorReporter.reportError("Failed to lock with file lock: " + lock.myLock.getAbsolutePath());
      return null;
    }

    public static void unlock(final FileLock lock) {
      unlock(lock, 5, 100);
    }

    public static void unlock(final FileLock lock, final int retries, final long waitTimeMS) {
      if (lock == null) return;
      for (int attempt = 0; attempt < retries; attempt++) {
        if (!lock.isLocked()) return;
        if (lock.tryUnlock()) return;
        wait(lock, waitTimeMS, "unlock");
      }
      ErrorReporter.reportError("Failed to unlock with file lock: " + lock.myLock.getAbsolutePath());
    }

    private static void wait(final FileLock lock, final long waitTimeMS, final String action) {
      try {
        Thread.sleep(waitTimeMS);
      } catch (InterruptedException e) {
        throw new RuntimeException("Failed to " + action + " with file lock: " + lock.myLock.getAbsolutePath(), e);
      }
    }
  }

  private static String readString(DataInput stream) throws IOException {
    int length = stream.readInt();
    if (length == -1) return null;

    char[] chars = new char[length];
    byte[] bytes = new byte[length*2];
    stream.readFully(bytes);

    for (int i = 0, i2 = 0; i < length; i++, i2+=2) {
      chars[i] = (char)((bytes[i2] << 8) + (bytes[i2 + 1] & 0xFF));
    }

    return new String(chars);
  }

  private static void writeString(DataOutput stream, String s) throws IOException {
    if (s == null) {
      stream.writeInt(-1);
      return;
    }
    char[] chars = s.toCharArray();
    byte[] bytes = new byte[chars.length * 2];

    stream.writeInt(chars.length);
    for (int i = 0, i2 = 0; i < chars.length; i++, i2 += 2) {
      char aChar = chars[i];
      bytes[i2] = (byte)((aChar >>> 8) & 0xFF);
      bytes[i2 + 1] = (byte)((aChar) & 0xFF);
    }

    stream.write(bytes);
  }

  private final static ThreadLocalCachedValue ioBuffer = new ThreadLocalCachedValue() {
    @Override
    protected byte[] create() {
      return allocReadWriteUTFBuffer();
    }
  };

  private static byte[] allocReadWriteUTFBuffer() {
    return new byte[STRING_LENGTH_THRESHOLD + STRING_HEADER_SIZE];
  }

  public static void writeUTF(final DataOutput storage, final String value) throws IOException {
    int len = value.length();
    if (len < STRING_LENGTH_THRESHOLD && isAscii(value)) {
      ioBuffer.getValue()[0] = (byte)len;
      for (int i = 0; i < len; i++) {
        ioBuffer.getValue()[i + STRING_HEADER_SIZE] = (byte)value.charAt(i);
      }
      storage.write(ioBuffer.getValue(), 0, len + STRING_HEADER_SIZE);
    }
    else {
      storage.writeByte((byte)0xFF);

      try {
        storage.writeUTF(value);
      }
      catch (UTFDataFormatException e) {
        storage.writeUTF(LONGER_THAN_64K_MARKER);
        writeString(storage, value);
      }
    }
  }

  public static String readUTFFast(final DataInput storage) throws IOException {
    int len = 0xFF & (int)storage.readByte();
    if (len == 0xFF) {
      String result = storage.readUTF();
      if (LONGER_THAN_64K_MARKER.equals(result)) {
        return readString(storage);
      }

      return result;
    }

    final char[] chars = new char[len];
    storage.readFully(ioBuffer.getValue(), 0, len);
    for (int i = 0; i < len; i++) {
      chars[i] = (char)ioBuffer.getValue()[i];
    }

    return new String(chars);
  }

  private static boolean isAscii(final String str) {
    for (int i = 0; i != str.length(); ++ i) {
      final char c = str.charAt(i);
      if (c >= 128) {
        return false;
      }
    }
    return true;
  }

   public static int readINT(DataInput record) throws IOException {
    final int val = record.readUnsignedByte();
    if (val < 192) {
      return val;
    }

    int res = val - 192;
    for (int sh = 6; ; sh += 7) {
      int next = record.readUnsignedByte();
      res |= (next & 0x7F) << sh;
      if ((next & 0x80) == 0) {
        return res;
      }
    }
  }

  public static void writeINT(DataOutput record, int val) throws IOException {
    /*
    if (0 <= val && val < 255)
      record.writeByte(val);
    else {
      record.writeByte(255);
      record.writeInt(val);
    }
    */
    if (0 <= val && val < 192) {
      record.writeByte(val);
    }
    else {
      record.writeByte(192 + (val & 0x3F));
      val >>>= 6;
      while (val >= 128) {
        record.writeByte((val & 0x7F) | 0x80);
        val >>>= 7;
      }
      record.writeByte(val);
    }
  }


  public static String collapse(String methodSignature, final DictionaryLookup dictionaryLookup) {
    return processWithDictionary(methodSignature, new Consumer() {
      protected String consume(String type) {
          final int dictionaryIndex = dictionaryLookup.getDictionaryIndex(type);
          return dictionaryIndex >= 0 ? String.valueOf(dictionaryIndex) : type;
      }
    });
  }

  private static final Pattern TYPE_PATTERN = Pattern.compile("L[^;]*;");

  public static abstract class Consumer {
    protected abstract String consume(String str);
  }

  static String processWithDictionary(String methodSignature, Consumer consumer) {
    final Matcher matcher = TYPE_PATTERN.matcher(methodSignature);
    while (matcher.find()) {
      String s = matcher.group();
      if (s.startsWith("L") && s.endsWith(";")) {
        final String type = s.substring(1, s.length() - 1);
        final String replacement = consumer.consume(type);
        //noinspection StringEquality
        if (replacement != type) {
          methodSignature = methodSignature.replace(type, replacement);
        }
      }
    }
    return methodSignature;
  }

  public static DataOutputStream openFile(File file) throws FileNotFoundException {
    return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
  }

  public static void close(OutputStream out) {
    if (out != null) {
      try {
        out.close();
      } catch (IOException ignored) {}
    }
  }

  public static void close(InputStream in) {
    if (in != null) {
      try {
        in.close();
      } catch (IOException ignored) {}
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy