Spring Boor Performance

Logging

  • hibernate.show_sql (niet in prod!)
  • hibernate.format_sql
  • hibernate.use_sql_comments

params: - org.hibernate.type.descriptor.sql level trace

P6Spy

pom.xml

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version> <!-- You can use the latest version -->
</dependency>

application.properties

spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password

databaseDialectDateFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ
customLogMessageFormat=%{currentTime}|%(executionTime)ms|%(category)|connection %(connectionId)|\n%(sqlSingleLine)

modulelist=com.p6spy.engine.outage.P6OutageFactory,com.p6spy.engine.logging.P6LogFactory
logfile=spy.log
logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
appender=com.p6spy.engine.spy.appender.Slf4JLogger
databaseDialectDateFormat=yyyy-MM-dd HH:mm:ss.SSS
•   modulelist: Specifies which P6Spy modules to enable.
•   logfile: Defines where the log file will be created.
•   appender: Determines where logs should be sent, such as a file or SLF4J for logging through a standard logging framework.

Datasource-proxy

@Bean
public DataSource dataSource() {
    SLF4JQueryLoggingListener loggingListener = new SLF4JQueryLoggingListener();
    loggingListener.setQueryLogEntryCreator(new InlineQueryLogEntryCreator());

    return ProxyDataSourceBuilder.create(actualDataSource())
        .name(DATA_SOURCE_PROXY_NAME)
        .listener(loggingListener)
        .build();
}
  • can have own cutom statement execution listeners

FetchSize

statement.setFetchSize(fetchSize) 10 Oracle 120 SQL PostreSQL MySQL whole resultset in single roundtrip

JPA2.2 getResultStream dan wel zetten voor PostgresQL en MySQL

Streams

MySQL streaming

One Record

statement.setFetchSize(Integer.MIN_VALUE)

Multiple Records

statement.setFetchSize(fetchSize)

Oracle

spring.jpa.properties.hibernate.jdbc.fetch_size=50

MySQL Postgres

@Query("""
    select p
    from Post p
    where date(p.createdOn) >= :sinceDate
""")
@QueryHints(
    @QueryHint(name = AvailableHints.HINT_FETCH_SIZE, value = "25")
)
Stream<Post> streamByCreatedOnSince(
    @Param("sinceDate") LocalDate sinceDate
);

Pagination

Data grows per page

  • FETCH FIRST N ROWS ONLY
  • FETCH NEXT N ROWS ONLY
  • OFFSET M ROWS Oracle 12c SQL2012 PostgresQL 8.4

let op ORDER BY

Top-N

SELECT title
FROM post
ORDER BY created_on DESC, id DESC
FETCH FIRST 5 ROWS ONLY

Next-N

SELECT title
FROM post
ORDER BY created_on DESC, id DESC
OFFSET 5 ROWS
FETCH NEXT 5 ROWS ONLY

PostgreSQL MySQL TOP-N

SELECT title
FROM post
ORDER BY created_on DESC, id DESC
LIMIT 5

PostgreSQL MySQL NEXT-N

SELECT title
FROM post
ORDER BY created_on DESC, id DESC
LIMIT 5
OFFSET 5

NB: OFFSET komt NA LIMIT!!!

JPQL Querying -Pagination

Page<Post> findAllByTitle(
    @Param("titlePattern") String titlePattern,
    Pageable pageRequest
);

@Query("""
    select p
    from Post p
    where p.title like :titlePattern
""")
Page<Post> findAllByTitle(
    @Param("titlePattern") String titlePattern,
    Pageable pageRequest
);

JPQL QUERY Pagination Top-N

Page<Post> posts = postRepository.findAllByTitle(
    "High-Performance Java Persistence %",
    PageRequest.of(0, 25, Sort.by("createdOn"))
);
SELECT p.id, p.created_on, p.title
FROM post p
WHERE p.title LIKE 'High-Performance Java Persistence %' ESCAPE ''
ORDER BY p.created_on ASC
OFFSET 0 ROWS
FETCH FIRST 25 ROWS ONLY
@Query(value = """
    SELECT p.id, p.title, p.created_on
    FROM post p
    WHERE p.title ilike :titlePattern
    ORDER BY p.created_on
""",
nativeQuery = true)
Page<Post> findAllByTitleLike(
    @Param("titlePattern") String titlePattern,
    Pageable pageRequest
);

Top-N

Page<Post> posts = postRepository.findAllByTitle(
    "High-Performance Java Persistence %",
    PageRequest.of(0, 25)
);
SELECT p.id, p.title, p.created_on
FROM post p
WHERE p.title ilike 'High-Performance Java Persistence %'
ORDER BY p.created_on
FETCH FIRST 25 ROWS ONLY

Offset pagination index scanning performance

CREATE INDEX idx_post_created_on ON post (created_on DESC, id DESC);

SELECT id
FROM post
ORDER BY created_on DESC
LIMIT 50;
Limit  (cost=0.28..2.51 rows=50 width=16)
  (actual time=0.013..0.022 rows=50 loops=1)
  -> Index Scan using idx_post_created_on on post p
     (cost=0.28..223.28 rows=5000 width=16)
     (actual time=0.013..0.019 rows=50 loops=1)
Planning time: 0.113 ms
Execution time: 0.055 ms

2e en latere scan

SELECT id
FROM post
ORDER BY created_on DESC
LIMIT 50
OFFSET 50;
Limit  (cost=2.51..4.74 rows=50 width=16)
  (actual time=0.032..0.044 rows=50 loops=1)
  -> Index Scan using idx_post_created_on on post p
     (cost=0.28..223.28 rows=5000 width=16)
     (actual time=0.022..0.040 rows=100 loops=1)
Planning time: 0.198 ms
Execution time: 0.071 ms

Nu 100rows!!! Op de laattste pagina wordt alles gescanned... 1.190ms..... OFFSET doesnt seek/traverse

Seek or keyset pagination

SELECT id, created_on
FROM post
ORDER BY created_on DESC, id DESC
LIMIT 50

created_on EN id moeten ERIN

Next-N

SELECT id, created_on
FROM post
WHERE (created_on, id) < ('2024-10-02 21:00:00.0', 4951)
ORDER BY created_on DESC, id DESC
LIMIT 50;
•   The row value expression (a, b) < (c, d) is PostgreSQL and MySQL.
•   It’s equivalent to a < c | (a = c & b < d).

Nu wel max 50 results in set

Spring Data JPA - WindowIterator

Oplossing!

WindowIterator<PostComment> commentWindowIterator = WindowIterator.of(
    position -> postCommentRepository.findByPost(
        post,
        PageRequest.of(
            0, pageSize,
            Sort.by(
                Sort.Order.desc(PostComment_.CREATED_ON),
                Sort.Order.desc(PostComment_.ID)
            )
        ),
        position
    )
).startingAt(ScrollPosition.keyset());

commentWindowIterator.forEachRemaining(this::processPostComment);

met Blaze Persistance Top-N

// Blaze Persistence – Keyset pagination
PagedList<Post> firstPage = cbf
    .create(entityManager, Post.class)
    .orderByAsc(Post_.CREATED_ON).orderByAsc(Post_.ID)
    .page(0, pageSize)
    .withKeysetExtraction(true)
    .getResultList();
SELECT p.id, p.created_on, p.title,
       (SELECT count(*) FROM post)
FROM post p
ORDER BY p.created_on, p.id
OFFSET 0
ROWS FETCH FIRST 25 ROWS ONLY;

Next N

// Blaze Persistence – Keyset pagination
PagedList<Post> nextPage = cbf
    .create(entityManager, Post.class)
    .orderByAsc(Post_.CREATED_ON).orderByAsc(Post_.ID)
    .page(postPage.getKeysetPage(),
          postPage.getPage() * postPage.getMaxResults(),
          postPage.getMaxResults())
    .getResultList();
SELECT p.id, p.created_on, p.title,
       (SELECT count(*) FROM post)
FROM post p
WHERE ('2024-09-09 12:10:00.0', 10) < (p.created_on, p.id)
ORDER BY p.created_on, p.id
OFFSET 0
ROWS FETCH FIRST 25 ROWS ONLY;

Projections

Fetching too many columns

Instead of fetching all columns:

SELECT *  
FROM post_comment pc  
LEFT JOIN post p ON p.id = pc.post_id  
LEFT JOIN post_details pd ON p.id = pd.id  


Fetch a custom SQL projection:


SELECT pc.id, pc.review FROM post_comment pc LEFT JOIN post p ON p.id = pc.post_id LEFT JOIN post_details pd ON p.id = pd.id

(Joins zijn niet nodig... => dus tweede is dan sneller)

Tuple projection

not type save

• The JPA Tuple wraps the default Object[] projection and allows you to retrieve the column values via their aliases.

List<Tuple> commentTuples = postRepository.findAllCommentTuplesByPostTitle(titleToken);
Tuple commentTuple = commentTuples.get(0);

long id = commentTuple.get("id", Number.class).longValue();
String title = commentTuple.get("title", String.class);

Interface-based projection

• If you want a type-safe projection, Spring Data JPA provides the option of wrapping the result in a Proxy based on a given interface.

The Interface-based projection can be used like this:

@Query("""
select
p.id as id,
p.title as title,
c.review as review
from PostComment c
join c.post p
where p.title like :postTitle
order by c.id
""")
List<PostCommentSummary> findAllCommentSummariesByPostTitle(
@Param("postTitle") String postTitle
);

// nu type safe!       
Long id = commentSummary.getId();
String title = commentSummary.getTitle();

Met ingebakken (!) DTOs en Records

DTOs

properties.put(
    "hibernate.integrator_provider",  // Hibernate property for custom integrator
    (IntegratorProvider) () -> Collections.singletonList( // Lambda to provide integrator
        new ClassImportIntegrator( // Hypersistence Utils integrator
            List.of( // List of classes to register
                PostCommentDTO.class,  // First DTO class
                PostCommentRecord.class // Second DTO class
            )
        )
    )
);

String jpql = "SELECT new PostCommentDTO(pc.id, pc.comment) FROM PostComment pc WHERE pc.post.id = :postId";


Records

public record PostCommentDTO(Long id, String comment) {}
public record PostCommentRecord(Long id, String content) {}

properties.put(
    "hibernate.integrator_provider",
    (IntegratorProvider) () -> Collections.singletonList(
        new ClassImportIntegrator(
            List.of(
                PostCommentDTO.class,   // Register the record as a DTO
                PostCommentRecord.class // Register additional records as needed
            )
        )
    )
);

@Query(
    value = "SELECT pc.id, pc.comment FROM post_comment pc WHERE pc.post_id = :postId",
    nativeQuery = true
)
List<PostCommentDTO> findCommentsByPostId(@Param("postId") Long postId);

Long id = commentRecord.id();
String title = commentRecord.title();

TupleTransformer and ResultListTransformer

The SQL projection can contain data from multiple tables, like the post and post_comment, which form a one-to-many table relationship.

SELECT p.id AS p_id,
p.title AS p_title,
pc.id AS pc_id,
pc.review AS pc_review
FROM post p
JOIN post_comment pc ON p.id = pc.post_id
ORDER BY pc.id

komt uit:

p_id p_title pc_id pc_review
1 High-Performance Java Persistence 1 Best book on JPA and Hibernate!
1 High-Performance Java Persistence 2 A must-read for every Java developer!
2 Hypersistence Optimizer 3 It's like pair programming with Vlad!

Spring snips

Docker

local

docker run -v ./app.jar:/app/app.jar \
-e DB_USERNAME='admin' -e DB_PASSWORD='password123' \
-p 8080:8080 \
amazoncorretto:21 \
java -jar /app/app.jar --spring.profiles.active=prod

start met .properties file

DB_USERNAME=user java -jar target/app.jar --spring.profiles.active=prod

Application properties

in application.prod.properties

spring.application.version=@project.version@

// h2
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.url=jdbc:h2:mem:contactdb
spring.h2.console.enabled=true

// MySQL
spring.datasource.url=jdbc:mysql://localhost:3306/tempdb?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=UTC&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=<YOURPASS>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate JPA settings
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# Logging level for debugging
logging.level.org.hibernate=DEBUG

logging.level.root=debug

actuator

voor in dev:

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.server.port=9090

Intellij Endpoint POST Request

POST http://localhost:8080/players
Accept: */*
Accept-Encoding: gzip, deflate
Content-Type: application/json
Accept-Language: en-us

{
 "role" : "value3",
 "name": "value4"
}

Lombok

Log4j2 Pattern Layout Alternative

configure Log4j2 to automatically include the class and method names in log messages by setting up a custom pattern in log4j2.xml:

<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %C{1}.%M - %msg%n"/>

Swagger

`` org.springdoc springdoc-openapi-starter-webmvc-ui 2.6.0 ```

http://localhost:8080/swagger-ui/index.html

en json format
http://localhost:8080/v3/api-docs

Profiles

in yaml -- maakt er feitelijk 2 yaml docs van

in .properties:

spring.profiles.active=sql
spring.config.activate.on-profile=sql

Kafka

https://kafka.apache.org/quickstart

// download
10015  tar -xzf kafka_2.13-3.8.0.tgz
// unpack and start
10016  cd kafka_2.13-3.8.0
10019  cd ~/Downloads/kafka_2.13-3.8.0
10020  bin/kafka-server-start.sh config/server.properties
10022  bin/kafka-topics.sh --create --topic quickstart-events --bootstrap-server localhost:9092
10023  bin/kafka-topics.sh --describe --topic quickstart-events --bootstrap-server localhost:9092
10024  bin/kafka-console-producer.sh --topic quickstart-events --bootstrap-server localhost:9092
10026  bin/kafka-console-consumer.sh --topic quickstart-events --from-beginning --bootstrap-server localhost:9092
10027  bin/kafka-server-start.sh config/server.properties
// consume
10029  bin/kafka-console-producer.sh --topic quickstart-events --bootstrap-server localhost:9092
10030  bin/kafka-console-consumer.sh --topic quickstart-events --from-beginning --bootstrap-server localhost:9092
10032  bin/kafka-topics.sh --describe --topic cab-location --bootstrap-server localhost:9092
10033  bin/kafka-console-consumer.sh --topic cab-location --from-beginning --bootstrap-server localhost:9092

application properties Driver

spring.application.name=kafkaBookingDriver
spring.kafka.producer.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
server.port=8082

application properties User

spring.application.name=kafkaBookingUser

spring.kafka.consumer.bootstrap-servers=localhost:9092
spring.kafka.consumer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.consumer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.consumer.auto-offset-reset=earliest
server.port=8081

spring.kafka.consumer.group-id=user-group

Driver

needs: - config

  • Controller

  • service

config:

package nl.appall.java.spring.kafkabookingdriver.config;

import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.TopicBuilder;

import static nl.appall.java.spring.kafkabookingdriver.constant.AppConstant.CAB_LOCATION;

@Configuration
public class KafkaConfig {


    @Bean
    public NewTopic topic() {
        return TopicBuilder
                .name(CAB_LOCATION)
                .build();
    }
}

controller:

package nl.appall.java.spring.kafkabookingdriver.controller;

import nl.appall.java.spring.kafkabookingdriver.service.CabLocationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/location")
public class CabLocationController {

    @Autowired private CabLocationService cabLocationService;

    @PutMapping
    public ResponseEntity updateLaction() throws InterruptedException {

        int range = 100;
        while(range > 0) {
            cabLocationService.updateLocation(Math.random() + " , "+ Math.random());
            Thread.sleep(1000);
            range--;
        }
        return new ResponseEntity<>(Map.of("message","Location Updated"), HttpStatus.OK);
    }
}

service:

package nl.appall.java.spring.kafkabookingdriver.service;

import nl.appall.java.spring.kafkabookingdriver.constant.AppConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class CabLocationService {

    @Autowired private KafkaTemplate<String,Object> kafkaTemplate;

    public boolean updateLocation(String location){
        kafkaTemplate.send(AppConstant.CAB_LOCATION, location);
        return true;
    }
}

User

needs only service

package nl.appall.java.spring.kafkabookinguser;

import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class LocationService {

    @KafkaListener(
            topics = "cab-location",
            groupId = "user-group"
    )
    public void cabLocation(String location){
        System.out.println(location);

    }
}

Tags: 

Sqlite

  • ALTER COLUMN is not supported. Official recommendation for changing a column? Make a new table.

  • DROP CONSTRAINT is not supported. Official recommendation for removing a constraint? Make a new table.

  • SQLite doesn’t have data types on columns. Data types (and there are only five) are on values only, so anything can go anywhere.

  • If you ask for a column with an unsupported/non-existent type, it happily does the wrong thing without warning or error. I was raising a schema like CREATE TABLE my_table (id bigserial, messages jsonb[]), which seemed to be working, so I mistakenly thought for the first day that SQLite supported serials and arrays. It does not.

  • You can use CREATE TABLE my_table (...) STRICT to only allow one of the five supported types: integer, real, text, blob, any.

  • There was a lot of recent fanfare about SQLite’s new support for jsonb. Unlike in Postgres, jsonb is not actually a data type, but rather a format that’s input and output to and from built-in jsonb* functions. When persisted, it’s one of the big five: blob.

Other fairly critical types are also missing. e.g. There’s nothing like timestamptz. If you want a date/time, you store is as a Unix timestamp integer or ISO8601-formatted string, and a number of built-in functions are provided to work with those.

https://brandur.org/atoms/gubk5w2

Tags: 

Custom Maven Starter Archetype

Create Maven Archetype

  • create an empty Maven project
mvn archetype:generate \
    -DgroupId=nl.appall.java \
    -DartifactId=java \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

  • modify the pom file with for example to add Junit5 and Java 11.
<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
    <!-- JUnit Jupiter API for writing tests -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.0</version>
        <scope>test</scope>
    </dependency>

    <!-- JUnit Jupiter Engine for running tests -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.7.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

  • Use the maven-archetype-plugin to create an archetype from your project:
mvn archetype:create-from-project
  • Deploy the generated archetype to a repository (local or remote) so it can be reused:
mvn install
  • create a Maven Settings file
mkdir -p ~/.m2
echo '<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <localRepository/>
  <interactiveMode/>
  <usePluginRegistry/>
  <offline/>
  <pluginGroups/>
  <servers/>
  <mirrors/>
  <proxies/>
  <profiles/>
  <activeProfiles/>
</settings>' > ~/.m2/settings.xml

use your archetype to generate a new project like:

mvn archetype:create-from-project

In bashrc

To automate the creation of a new blank project with your custom archetype you can update your bashrc like ~/.zshrc

march() {
         if [ -z "$1" ]; then
             echo "Error: No project name provided."
             echo "Usage: march <projectname>"
            return 1
        fi

        mvn archetype:generate \
            -DarchetypeGroupId=nl.appall.java \
            -DarchetypeArtifactId=java-archetype \
            -DarchetypeVersion=1.0-SNAPSHOT \
            -DgroupId=nl.appall.newproject \
            -DartifactId=$1 \
            -Dversion=1.0-SNAPSHOT \
            -DinteractiveMode=false

        cd $1
        idea .
   }

now march yourProject creates the project , gets in the folder and starts intellij.

Navigation from and to views

struct ContentView: View {
    @State private var selection: Int? = 0

    var body: some View {
        NavigationView {
            VStack {
                if selection == 0 {
                    FirstView(selection: $selection)
                } else if selection == 1 {
                    SecondView(selection: $selection)
                } else if selection == 2 {
                    ThirdView(selection: $selection)
                }
            }
        }
    }
}

struct FirstView: View {
    @Binding var selection: Int?

    var body: some View {
        Button("Go to Second View") {
            selection = 1
        }
    }
}

struct SecondView: View {
    @Binding var selection: Int?

    var body: some View {
        Button("Go to Third View") {
            selection = 2
        }
    }
}

struct ThirdView: View {
    @Binding var selection: Int?

    var body: some View {
        Button("Go to First View") {
            selection = 0
        }
    }
}



#Preview {
    ContentView()
}

SinglyLinkedList

class Node<T> {
    T data;
    Node<T> next;

    public Node(T data) {
        this.data = data;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(data.toString());
        Node<T> current = next;
        while (current != null) {
            sb.append(", ");
            sb.append(current.data.toString());
            current = current.next;
        }
        sb.append("]");
        return sb.toString();
    }
}

public class SinglyLinkedList<T> {
    private Node<T> head;

    public SinglyLinkedList() {
        this.head = null;
    }

    public void add(T data) {
        Node<T> newNode = new Node<>(data);
        if (head == null) {
            head = newNode;
        } else {
            Node<T> current = head;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
        }
    }

    public boolean remove(T data) {
        if (head == null) {
            return false; // List is empty
        }
        if (head.data.equals(data)) {
            head = head.next;
            return true;
        }

        Node<T> current = head;
        while (current.next != null) {
            if (current.next.data.equals(data)) {
                current.next = current.next.next;
                return true;
            }
            current = current.next;
        }
        return false; // Element not found
    }

    public T get(int index) {
        if (index < 0 || head == null) {
            throw new IndexOutOfBoundsException("Index out of bounds or list is empty.");
        }

        Node<T> current = head;
        int currentIndex = 0;
        while (current != null) {
            if (currentIndex == index) {
                return current.data;
            }
            current = current.next;
            currentIndex++;
        }

        throw new IndexOutOfBoundsException("Index out of bounds.");
    }

    public boolean isEmpty() {
        return head == null;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        Node<T> current = head;
        while (current != null) {
            sb.append(current.data);
            if (current.next != null) {
                sb.append(", ");
            }
            current = current.next;
        }
        sb.append("]");
        return sb.toString();
    }

    public Node middle() {
        if (head == null) {
            return null; // List is empty
        }

        Node<T> slow = head;
        Node<T> fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        return slow;
    }
}

SinglyLinkedList sll = new SinglyLinkedList<Integer>();
System.out.println("sll "+sll);
sll.add(1);
sll.add(2);
sll.add(3);
sll.add(4);
System.out.println("sll "+sll);
var middle = sll.middle();
//System.out.println("middle "+middle.toString());
//sll []
//sll [1, 2, 3, 4]
//middle [3, 4]

Join wisely

Joins

select distinct users.* from users
left join post users.i = posts.user_id
where views > 90000

// 800 ms

Sub Query1

select * from users
where id in (
  select user_id from posts where views > 90000
)

// 300ms Never use a sub query?

Since MySql 8 not true.

Sub Query 2

select * from users where exists ( 
  select user_id from posts where views > 90000 and users.id = user_id 
)  

// 200ms

create bigfile in cli

create bigfile in cli:

tr -dc "A-Za-z 0-9" < /dev/urandom | fold -w100|head -n 1000000 > bigfile.txt
Tags: 

Bun

Node Js

  • Node team is klein team en onbetaald
  • Voortgang Node versies gaat vooral over niet te veel breaken met voorgaande versies
  • Hierdoor blijven kritisch perfomance issues liggen
  • Gebouwd op de V8 engine. Kijk bv naar de code van de shell

Bun

  • Het Bun team zet in op performance
  • Heeft links met React en Vue
  • Probeert het hele eco systeem van Node te kunnen draaien
  • Kort gezegd een snellere Node

Klachten over Node

  • single threaded & concurrency
  • callbacks
  • Loops
  • Limited standard Libs
  • CPU perfomant tasks & Memory consumption
  • debugging
  • gemopper over update cycle en deprecations

Bun Safari Engine

Safari engine voordelen:
- batterij
- performance webkit
- security
- CJS and ES6 and beyond support
- cross-platform
- kleiner en sneller
- voor de browser

Safari


Wat is Bun nou?

het staat op : Bun Home Page Op Youtube zeggen ze zelf: - Bun is still under development. - Use it to speed up your development workflows or run simpler production code in resource-constrained environments like serverless functions.

- We're working on more complete Node.js compatibility and integration with existing frameworks.

Wat Bun is nou

  • Bun is an all-in-one toolkit for JavaScript and TypeScript apps. It ships as a single executable called bun​.
  • At its core is the Bun runtime, a fast JavaScript runtime designed as a drop-in replacement for Node.js.
  • It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage.
  • Werkt (nog) niet op Windows

Bun Install

Op Linux achtige machines:

# with install script (recommended)  
curl -fsSL https://bun.sh/install | bash  

# with npm  
npm install -g bun  

# with Homebrew  
brew tap oven-sh/bun  
brew install bun  

# with Docker  
docker pull oven/bun  
docker run --rm --init --ulimit memlock=-1:-1 oven/bun  

bun init

Na de check of bun t doet:

bun --version  

Kan je:

bun init

Bun commands

Net zoals npm

upgrade

bun upgrade    

To install dependencies:

bun install
bun i

bun start inhoud folder

branche 01_init na

bun init

bevat de folder

.gitignore
README.md
bun.lockb
index.ts
node_modules
package.json
tsconfig.json


Bun run

hierna klaar voor:

bun run index.ts    

Bun imports

branche 02_say_hello

import van andere ts file


Bun serve

branch 03_bun_serve

start een server op zoals in bv express


Bun scripts

branch 04 bun run start

"scripts": {  
  "start": "bun run index.ts"  
},

Bun Scripts 2

{  
  // ... other fields  
  "scripts": {  
    "clean": "rm -rf dist && echo 'Done.'",  
    "dev": "bun server.ts"  
  }  
}  

bun run clean  
bun run dev  

Bun tsconfig

branch 5 dom en tsconfig

rare errors van mn lsp kon ik oplossen met:

/// <reference lib="dom" />  
/// <reference lib="dom.iterable" />

Bun run

bun run index.ts    

To run a file in watch mode, use the --watch flag.

bun --watch run index.tsx  

Scripts

{  
  // ... other fields  
  "scripts": {  
    "clean": "rm -rf dist && echo 'Done.'",  
    "dev": "bun server.ts"  
  }  
}  

bun run clean  
bun run dev  

Bun test

Bun pagina Run tests

bun test  

Tests are written in JavaScript or TypeScript with a Jest-like API. Refer to Writing tests for full documentation.


Bun test 2

The runner recursively searches the working directory for files that match the following patterns:

  • *.test.{js|jsx|ts|tsx}
    *_test.{js|jsx|ts|tsx}
  • *.spec.{js|jsx|ts|tsx}
  • *_spec.{js|jsx|ts|tsx}

You can filter the set of test files to run by passing additional positional arguments to bun test.


Test example

  • 06_test branche* math.test.ts
import { expect, test } from "bun:test";  

test("2 + 2", () => {  
  expect(2 + 2).toBe(4);  
});  

Elysia

Elisia De Express van / met Bun

bun create elysia app

Elysia 2

  • Performance : You shall not worry about the underlying performance
  • Simplicity : Simple building blocks to create an abstraction, not repeating yourself
  • Flexibility : You shall be able to customize most of the library to fits your need

eenvoudig integratie met swagger


Performance For Loops

branche 07 for loops

start de node versie

node forloop.js

start de bun versie

bun start

Performance server

express bun en elysia

met artillery

ontloopt elkaar niet heel veel

voordelen op openshift?

toon results


Project met Tailwind

toon crm project

voordeel alles wordt razend snel door bun gecompileerd

Bun runt project en de bundle

css wordt gegenereerd door Tailwind


Performance packages en cache

youtube en bun pagina vol met voorbeelden.


Toekomst

Node en Bun zullen voorlopig naast elkaar bestaan

Bun is nog niet productie klaar

Geen Windows versie


Bun en Angular

Installeer new project met ng new projectName daarna cd de map in en bun i
De eerste keer duurt dat even lang als npm i.
bun start om het project te starten. Duurt de eerste keer ook even lang als npm start. Als je nu
rm package-lock.json && rm -rf /node_modules
en daarna weer bun i && bun start dan gaat alles wel veel sneller (op mijn systeem bunnen 3s ).
Vooral handig als je snel je node_modules wil verwijderen en weer opnieuw wil installeeren zoals bv bij het upgraden van Angular.


Github

AWK

Awk operates on a per-line basis, where it reads input line by line, applies patterns and actions, and then produces output based on those patterns.

Extracts disk usage percentage for the root file system

df -h | awk '$NF == "/" {print "Usage:", $5}'

Lists processes using more than 10% CPU

ps aux | awk '{if ($3 >= 10) print $0}'

Monitors CPU and memory usage of a process by PID

top -p PID -n 1 | awk '/PID/{getline; print "CPU:", $9, "MEM:", $10}'

Monitors CPU and memory usage of a specific user

awk -F':' '{ print "User:", $1, "Home:", $6 }' /etc/passwd

Extracts lines containing "error" from syslog

awk '/error/ {print $0}' /var/log/syslog

Displays network interfaces and their IP addresses

ifconfig | awk '/inet addr/{print "Interface:", $1, "IP:", $2}'

Checks if a service is active or not

systemctl is-active service-name | awk '{print "Service Status:", $0}'

Shows system uptime

uptime | awk '{print "Uptime:", $3, $4}'

Displays total and used memory

free -m | awk '/Mem/{print "Total Memory:", $2 "MB", "Used Memory:", $3 "MB"}'

Lists open ports in listening state

netstat -tuln | awk '/LISTEN/{print "Port:", $4}'

Lists top 5 largest directories

du -h /path | sort -rh | awk 'NR<=5{print $2, $1}'

Displays CPU model information

awk -F: '/model name/ {print "CPU Model:", $2}' /proc/cpuinfo

Monitors disk I/O for a specific disk (e.g., sda)

iostat -x 1 | awk '$1=="sda" {print "Read:", $6 "KB/s, Write:", $7 "KB/s"}'

Lists active network connections and their states

ss -tuln | awk 'NR>1 {print "Protocol:", $1, "State:", $2, "Local Address:", $4}'

Displays system load averages

uptime | awk -F'[a-z]:' '{print "Load Average (1/5/15 min):", $2, $3, $4}'

Captures CPU and memory usage snapshot

top -b -n 1 | awk '/Cpu/{print "CPU Usage:", $2 "%"}; /KiB Mem/{print "Memory Usage:", $8}'
Tags: 

Brew

brew list

View Dep tree

for package in $(brew leaves); do echo $package; brew deps $package; echo; done 

Leaves

List top-level installed packages

brew leaves

with sizes:

brew list --formula | while read cask; do echo -n "$cask: "; du -sh $(brew --prefix)/Cellar/$cask; done

Update & Upgrade

brew update && brew upgrade 

Brew Info

brew info 
Tags: 

Db2 SQL

Like

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '%pattern%';

Additionally, DB2 supports some additional wildcard characters that can be used with the LIKE operator:

  • _ (underscore): Matches any single character. For example, '_at' will match "cat", "bat", but not "at" or "rat".
  • [] (brackets): Matches any single character within the specified range or set. For example, '[abc]' will match "a", "b", or "c".
  • [^] (caret within brackets): Matches any single character not within the specified range or set. For example, '[^abc]' will match any character except "a", "b", or "c".
SELECT column1, column2
FROM table_name
WHERE column1 LIKE 'prefix%';

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '%suffix';

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '%part1_part2%';

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '_at';

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '[abc]at';

SELECT column1, column2
FROM table_name
WHERE column1 LIKE '[^abc]at';

Auto-incrementing Columns:

MySQL uses the AUTO_INCREMENT keyword to define a column that automatically increments its value for each new row inserted into a table. In DB2, the equivalent functionality is achieved using an identity column, which is defined as GENERATED ALWAYS AS IDENTITY.

Handling of NULL Values:

MySQL treats NULL values in a unique way when it comes to comparisons and sorting. It considers NULL as the lowest possible value, so NULL values come first when sorting in ascending order and last when sorting in descending order. DB2 follows the SQL standard behavior for NULL values, where they are considered unknown and are generally sorted at the end regardless of the sorting order.

String Comparison and Collation:

MySQL provides various collation options to define how string comparisons should be performed, taking into account factors such as case-sensitivity and accent sensitivity. DB2 also supports collations for string comparisons, but the default collation is determined by the database and operating system settings. Changing the collation requires altering the database or table collation settings.

Date and Time Functions:

MySQL and DB2 have some differences in their supported date and time functions. For example, MySQL provides functions like DATE_FORMAT() for formatting dates, UNIX_TIMESTAMP() for retrieving the current Unix timestamp, and NOW() for obtaining the current date and time. DB2 uses different functions, such as VARCHAR_FORMAT() for formatting dates, CURRENT_TIMESTAMP for retrieving the current timestamp, and CURRENT DATE for obtaining the current date.

Pagination Syntax:

When retrieving a limited subset of rows from a query result, MySQL uses the LIMIT clause to specify the number of rows to return and an optional offset to skip rows. In DB2, the equivalent functionality is achieved using the FETCH FIRST clause along with the ROWS keyword to specify the number of rows to return and the OFFSET clause to skip rows.

Java Curl

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class CurlExample {
    public static void main(String[] args) {
        try {
            // Create a URL object with the desired endpoint
            URL url = new URL("https://api.example.com/some-endpoint");

            // Open a connection to the URL
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // Set the request method (GET, POST, etc.)
            connection.setRequestMethod("GET");

            // Send the request and receive the response
            int responseCode = connection.getResponseCode();

            // Check if the request was successful (status code 200)
            if (responseCode == HttpURLConnection.HTTP_OK) {
                // Read the response
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                StringBuilder response = new StringBuilder();

                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }

                // Close the reader
                reader.close();

                // Print the response
                System.out.println(response.toString());
            } else {
                System.out.println("Request failed with response code: " + responseCode);
            }

            // Disconnect the connection
            connection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Maven conflicts

Maven is a popular build automation and dependency management tool used primarily in Java projects. It simplifies the process of building, packaging, and managing dependencies for Java applications. When you encounter conflicting versions of the same dependency in Maven, it means that different modules or libraries within your project require different versions of the same dependency.

Conflicting versions of dependencies can lead to issues like runtime errors, unexpected behavior, or incompatibilities between modules. Maven provides a mechanism to handle such conflicts through a concept called dependency resolution.

Here are a few key points to understand about Maven's dependency resolution process:

  1. Dependency Hierarchy: Maven maintains a dependency hierarchy, which is a tree-like structure representing all the direct and transitive dependencies of your project. Each node in the tree represents a specific version of a dependency.
  2. Dependency Mediation: When multiple versions of the same dependency are encountered, Maven applies a strategy called dependency mediation to determine which version to use. By default, Maven selects the "nearest definition," which means it chooses the version closest to your project in the dependency hierarchy.
  3. Dependency Exclusion: In some cases, you may want to exclude a specific version of a dependency from being used altogether. You can explicitly exclude a dependency by specifying an exclusion rule in your project's Maven configuration file (pom.xml).
  4. Dependency Management: Maven allows you to define a dependency management section in your project's pom.xml. This section lets you specify the versions of dependencies that should be used across all modules in your project. By centralizing version management, you can avoid conflicts and ensure consistent dependency versions throughout your project.

5.Forced Dependency Versions: In situations where you need to enforce the use of a specific version of a dependency, you can use the <dependencyManagement> section to explicitly declare the desired version. This approach can help resolve conflicts by ensuring that all modules use the specified version.

Resolving conflicting dependencies can be a complex task, especially in larger projects with numerous dependencies. It requires careful analysis of the dependency hierarchy, understanding the requirements of different modules, and making appropriate adjustments to your project's configuration.

Maven provides commands like mvn dependency:tree to display the dependency tree, which can help identify conflicting versions and their paths. Additionally, tools like the Maven Dependency Plugin can assist in managing dependencies and resolving version conflicts more effectively.

Overall, Maven's dependency management capabilities aim to simplify the process of handling dependencies in Java projects, but it's important to understand the concepts mentioned above to manage conflicting versions effectively.

Get Jar/File from class

The Java reflection API provides information about the structure of classes, methods, fields, etc., but it does not provide direct access to the underlying JAR file or file name.

However, you can still get the class file path or JAR file path from the class's ProtectionDomain and CodeSource using reflection. Here's an example:


import java.lang.reflect.CodeSource; import java.lang.reflect.Method; import java.security.ProtectionDomain; public class ClassName { public static void main(String[] args) { Class<?> clazz = ClassName.class; ProtectionDomain protectionDomain = clazz.getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); if (codeSource != null) { String location = codeSource.getLocation().getPath(); if (location.endsWith(".jar")) { String jarName = location.substring(location.lastIndexOf("/") + 1); System.out.println("JAR file name: " + jarName); } else { String className = clazz.getName().replace('.', '/') + ".class"; String fileName = location.substring(0, location.length() - className.length()); System.out.println("File name: " + fileName); } } else { System.out.println("Unable to determine file or JAR name."); } } }

Git master main

Rename to main

git branch -m main 

push to origin

git push origin -u main

Update config

git config --global init.defaultBranch main

delete master

git branch -d master
Tags: 

JUnit 5 CamelCase test func names.

JUnit 5 CamelCase Replace with spaces for test methods.

@DisplayNameGeneration(ReplaceCamelCase.class)
class DemoUtilsTest {

    DemoUtils demoUtils;

    @BeforeAll
    static void setupBeforeAll() {
        System.out.println("before all");
    }

    @AfterAll
    static void tearDownAfterAll() {
        System.out.println("after all");
    }
    @BeforeEach
    void setUp() {
        System.out.println("setup each");
        demoUtils = new DemoUtils();
    }

    @AfterEach
    void tearDown() {
    }
}

Replace Camel Case Class


import org.junit.jupiter.api.DisplayNameGenerator; import java.lang.reflect.Method; class ReplaceCamelCase extends DisplayNameGenerator.Standard { public ReplaceCamelCase() { } public String generateDisplayNameForClass(Class<?> testClass) { return this.replaceCapitals(super.generateDisplayNameForClass(testClass)); } public String generateDisplayNameForNestedClass(Class<?> nestedClass) { return this.replaceCapitals(super.generateDisplayNameForNestedClass(nestedClass)); } public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) { return this.replaceCapitals(testMethod.getName()); } String replaceCapitals(String camelCase) { StringBuilder result = new StringBuilder(); result.append(camelCase.charAt(0)); for (int i=1; i<camelCase.length(); i++) { if (Character.isUpperCase(camelCase.charAt(i))) { result.append(' '); result.append(Character.toLowerCase(camelCase.charAt(i))); } else { result.append(camelCase.charAt(i)); } } return result.toString(); } }

Baeldung

Conda

search on system:

conda search "^python$"

or

conda list | grep "^python"
conda create --name py311 python=3.11
conda config --set default_env py311
conda activate py311

check what version

python -V
pip -V

pip upgrade

pip install -U pip

SwiftUI NavigationStack + NavigationDestination

How NOT to implement the new NavigationStack NavigationDestination

import SwiftUI

struct ContentView: View {
    let arrayNums = [
        LItem(name: "1"),
        LItem(name: "2"),
        LItem(name: "3")
    ]
    var body: some View {
        NavigationStack {
            List{
                ForEach(arrayNums) { litem in
                    NavigationLink("nav link \(litem.name)", value: litem)
                }.navigationDestination(for: LItem.self) { tview in
                    TView(text: tview.name)
                }
            }

        }
    }
}

struct LItem: Codable, Equatable, Identifiable, Hashable {
    var id = UUID().uuidString
    let name: String
}
struct LView: View {
    var body: some View {
        TView(text: "in lview")
    }
}
struct TView: View {
    var text: String
    init(text: String) {
        print("init Tview \(text)")
        self.text = text
    }
    var body: some View {
        Text("\(text)")
    }
}

when opening the first link... All links are opened/instantiated?
All the links are added to the navigationstack and loaded?
log:

init Tview 3
init Tview 2
init Tview 1
init Tview 3
init Tview 2
init Tview 1
init Tview 3
init Tview 2
init Tview 1

when pushing the back button you will navigate to Tview 2 and TView 3?
seems like the assumption that all the links are in the nav stack and loaded is right..

ChatGPT solution is to step away from the new navigationstack/destination:

struct ContentView: View {
    let arrayNums = [        LItem(name: "1"),        LItem(name: "2"),        LItem(name: "3")    ]
    var body: some View {
        NavigationView {
            List {
                ForEach(arrayNums) { litem in
                    NavigationLink(
                        destination: TView(text: litem.name),
                        label: {
                            Text("nav link \(litem.name)")
                        })
                        .id(litem.id) // use id to force SwiftUI to create a new instance of TView
                }
            }
        }
    }
}

the logging will display now only:

init Tview 1
init Tview 1
init Tview 2
init Tview 2
init Tview 3
init Tview 3

This must be from the List component. When adding a .omAppear with text the log shows:

init Tview 1
init Tview 1
init Tview 2
init Tview 2
init Tview 3
init Tview 3
TView with text 1 appeared

Docker dbs

Mongo Db

docker pull mongo
docker run -d --name mongodb -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=mypassword mongo

Postgres Db

docker run --name postgresdb -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=secret -d postgres
docker run --name pgadmin -p 8080:80 -e 'PGADMIN_DEFAULT_EMAIL=user@domain.com' -e 'PGADMIN_DEFAULT_PASSWORD=secret' -d dpage/pgadmin4

MySQL Db

docker run --name mysql-db -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql

Use:

docker exec -it postgres psql -U postgres
docker exec -it mongosh

New TS proj

npm init
npm install npm@latest -g
tsc --init --sourceMap --rootDir src --outDir dist
mkdir src
cd src
touch index.ts

npm i save-dev typescript point source in tsconfig.json to the outDir: dist & rootDir: src

add another ts file

compile with

 tsc

package.json "main": "index.js"

intellij set language to typescipt in settings recompile on changes

Weakify

Weakify

from video: https://www.youtube.com/watch?v=BGzPK7f13RM
gist: https://gist.github.com/vincent-pradeilles/86b32d3341aa47bcf6ae46d8d726f2f7

import UIKit

class Service {
    func call(with completion: @escaping (Int) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            completion(42)
        }
    }
}

class ViewController: UIViewController {
   
    let service = Service()
    let label = UILabel()
   
    func format(data: Int) -> String {
        return "\(data)"
    }
   
    override func viewDidLoad() {
        super.viewDidLoad()
       
        label.text = "Retrieved data: "
       
        service.call(with: weakify { strongSelf, data in
            /* Specific logic */
            let formatted = strongSelf.format(data: data)
            strongSelf.label.text?.append(formatted)
            /* End of specific logic */
        })
    }
}

protocol Weakifiable: class { }

extension NSObject: Weakifiable { }

extension Weakifiable {
    func weakify<T>(_ code: @escaping (Self, T) -> Void) -> (T) -> Void {
        return {
            /* Begin boilerplate */
            [weak self] (data) in
           
            guard let self = self else { return }
            /* End boilerplate */
           
            code(self, data)
        }
    }
}

Go on raspberry pi

Download page

Golang download

Latest package

latest package for ARM v6

mkdir ~/src && cd ~/src
wget https://dl.google.com/go/go1.14.4.linux-armv6l.tar.gz
sudo tar -C /usr/local -xzf go1.14.4.linux-armv6l.tar.gz
rm go1.14.4.linux-arm64.tar.gz
vi ~/.profile

adjust PATH

PATH=$PATH:/usr/local/go/bin
GOPATH=$HOME/go

update shell

source ~/.profile

Making a vst plugin from scratch in xcode

From: http://teragonaudio.com/article/Making-a-VST-plugin-from-scratch-with-Xc...

Making a VST plugin from scratch with Xcode
Introduction
Developing VST plugins under Mac OSX is in many ways simpler than other platforms, but nonetheless there are a few “gotchas” along the way.

This guide assumes familiarity with Xcode and C++ development, and that you are working with Mac OSX 10.5 or greater and have a relatively recent version of Xcode (4.2 or better). This guide only covers building VST 2.x plugins, as the VST3 SDK is not widely supported yet.

Also, before you start, you will obviously need the VST SDK, which can be acquired from Steinberg’s Development Portal. Note that Steinberg’s website is a bit confusing and does not label the downloads clearly, so make sure that you get the right version of the SDK.

Creating your project
First, create a new empty Xcode project. Now add a new target to the project, which should use the “Bundle” template under the “Framework & Library” group (for Mac OS X, of course). Set the product name, bundle identifier, and choose to link it against the Cocoa Framework.

Adding resource files
Create a new empty file in your project named “PkgInfo” with the contents “BNDL????” (no quotes, and no newline either). You can verify that this file will be copied to the VST bundle by clicking on your project’s target in the file browser, and then expanding the “Copy Bundle Resources” section underneath “Build Phases”.

When you created the project, Xcode should also have created a property list (plist) file for you. Open the plist file for editing, and right click anywhere in document body to change the view type to “Show Raw Keys/Values”. Now, set the following properties, adding new keys if necessary:

CFBundleDevelopmentRegion: English
CFBundleExecutable: vst
CFBundleGetInfoString: vst
CFBundleIconFile: (empty)
CFBundleIdentifier: com.yourcompany.pluginname
CFBundleInfoDictionaryVersion: 6.0
CFBundlePackageType: BNDL
CFBundleSignature: (A unique 4-character identifier of your choosing)
CFBundleVersion: 1.0
CSResourcesFileMapped: (empty)
Adding the VST SDK files
Create a new group for the VST source files, and drag them from the Finder into your project. Do not drag the entire vstsdk2.4 folder into your project. Make sure that the subfolders for “pluginterfaces” and “public.sdk” (excluding the samples) are in the project.

Now, in the project’s properties, go to the “Search Paths” section and add the vstsdk2.4 directory to the “Header Search Paths” setting. Make it recursive.

Project build settings
Unless you have very specific requirements, I highly recommend building your plugin as a standard 32-bit Intel binary. My reasoning for this is as follows:

Although 64-bit Macs are widespread, there are not so many 64-bit compatible plugin hosts, though this is slowly changing.
Likewise, building 64-bit VST’s is sometimes a bit difficult, as Apple is deprecating Carbon, which is 32-bit only.
The number of PPC users out there is not so many anymore, so building a 32-bit Universal Binary is probably overkill.
You can set the build type in the “Architectures” section, and again I recommend setting this to “32-bit Intel”. If anyone can get VST2.4 plugins building as 32/64 bit UB’s, please let me know so I can adapt this documentation to include how to do this.

Next, set the Base SDK to “Current Mac OS”. This will make it much less painful when opening the project in future versions of Xcode. In the “Deployment” section, set “Mac OS X Deployment Target” to the oldest version of Mac OS X you plan to support. Setting it to “Compiler Default” is likely to get you into trouble.

Under “Packaging”, make sure that both “Executable Extension” and “Executable Prefix” are empty. Set “Wrapper Extension” to be “vst”.

Frameworks
Again, in your target’s settings, go to the “Build Phases” tab and expand the “Link Binary With Libraries” section. Add the following libraries to your project:

QuickTime
Carbon
ApplicationServices
Your source code
Now you are ready to add or create files for your plugin’s source code.

Base62 in Go

The stdlib’s math/big package gives you a base62 implementation out of the box, you don’t need an external dependency for it, assuming you can hold the entire base62 dataset in memory. This is good enough for things like parsing/serializing UUIDs in a more compact format:

package main

import (
"fmt"
"math/big"
)

type UUID = [16]byte

func toBase62(uuid UUID) string {
var i big.Int
i.SetBytes(uuid[:])
return i.Text(62)
}

func parseBase62(s string) (UUID, error) {
var i big.Int
_, ok := i.SetString(s, 62)
if !ok {
return UUID{}, fmt.Errorf("cannot parse base62: %q", s)
}

var uuid UUID
copy(uuid[:], i.Bytes())
return uuid, nil
}

func main() {
fmt.Println(toBase62(UUID{0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1}))
fmt.Println(parseBase62("7N3zSy9F5jGaYc4BZR44Sd"))

  // Output:
  // 7N3zSy9F5jGaYc4BZR44Sd
  // [255 255 0 0 0 0 0 0 255 255 0 0 0 0 0 1] <nil>
}

audio stereo to mono script

Convert all files as duo mono files

create a folder in which you want to convert the stereo clips to mono.

#!/bin/bash

for f in .wav; do ffmpeg -i "$f" \
-filter_complex "[0:a]channelsplit=channel_layout=stereo[L][R]" \
-map "[L]" "${f%.
}_EXTRACTED_Lt.wav" \
-map "[R]" "${f%.*}_EXTRACTED_Rt.wav"; done

cli tree met find

find

find -maxdepth 2 -type d -ls | sed -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' > tree.txt

in tree text file
--test
----test.git
--apps
----namefile1.git
----namefile2.git

ls

ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'

|-folder
|---dir
|---dir2

Mutating Core Data object

Problem: How to mutate a fetched CD object in child views


import SwiftUI import CoreData struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], animation: .default) var items: FetchedResults<Item> var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { ItemDetail(item: item) } label: { Text("itemview1 at \(item.timestamp!, formatter: itemFormatter) name: \((item.name ?? "").isEmpty ? "": item.name! )") } } .onDelete(perform: deleteItems) } .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarTrailing) { EditButton() } #endif ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } } private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } private func deleteItems(offsets: IndexSet) { withAnimation { offsets.map { items[$0] }.forEach(viewContext.delete) do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct ItemDetail: View { @ObservedObject var item: Item @State var name: String = "" var body: some View { Form { TextField("Enter name", text: $name ).onChange(of: name) { newValue in item.name = name } Button("Submit") { // nav back } Spacer() NavigationLink { ItemDetail(item: item) } label: { Text("itemview1 at \(item.timestamp!, formatter: itemFormatter) name: \((item.name ?? "").isEmpty ? "": item.name! )") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) } }

find with grep

grep -rnw '/path/to/somewhere/' -e 'pattern'

include

grep --include=\*.{c,h} -rnw '/path/to/somewhere/' -e "pattern"

exclude

grep --exclude=\*.o -rnw '/path/to/somewhere/' -e "pattern"

with dir

grep --exclude-dir={dir1,dir2,*.dst} -rnw '/path/to/somewhere/' -e "pattern"
Tags: 

vst solve on mac with xattrib

When Mac OSX is complaining about a vst

user

sudo xattr -rd com.apple.quarantine /Users/<usrname>/Library/Audio/Plug-Ins/Components/Henrietta\ Smith-Rolla\ -\ Spectrum.vst3

system

sudo xattr -rd com.apple.quarantine /Library/Audio/Plug-Ins/VST3/Henrietta\ Smith-Rolla\ -\ Spectrum.vst3

Pages

Subscribe to hjsnips RSS